News, examples, tips, ideas and plans.
Thoughts around ORM, .NET and SQL databases.

Showing posts with label documentation. Show all posts
Showing posts with label documentation. Show all posts

Wednesday, October 10, 2012

Advanced mapping in DataObjects.Net 4.6 — Part 1

This article starts a series of posts covering advanced mapping capabilities introduced in DataObjects.Net 4.6.

Idea

The idea of advanced mappings is simple — to have a way to spread groups of persistent classes among different database schemas and even different databases according to number of mapping rules.

Basics

Advanced mapping configuration is based on set of mapping rules, each of them defines how to map a particular entity. Mapping rule consists of two logical parts:
  • condition (defines when the rule is applied)
  • mapping (specifies in what database/schema to place the target)
The rules could be specified in configuration file or right in code using fluent API. They are processed in the order they appear, so the first matching rule wins.

In addition to collection of rules, two properties specify default mapping: DefaultSchema and DefaultDatabase. These properties are used as fallback values if no rule matches entity being processed. Note, that 'defaultSchema' option is mandatory if any rules for schemas are specified.

XML configuration

OK, we are done with theory. Let's write some real mapping configurations. The most trivial one is setting the default schema. This is already supported by DataObjects.Net since version 4.3. Let's recall it:

<domain defaultSchema="myapp" ... />

Starting from version 4.6, we can write more precise mapping rules. They are defined in <mappingRules> element inside domain configuration.

<domain defaultSchema="myapp" ... >
  <mappingRules>
    <rule namespace="MyApp.Model.Foo" schema="myapp_foo" />
  </mappingRules>
</domain>

The above example maps all types from namespace 'MyApp.Model.Foo' to schema 'myapp_foo'. All other types are mapped to schema 'myapp' (fallback rule). If you have model with multiple assemblies in might be convenient to map to assembly names instead of namespaces:

<domain defaultSchema="mysite" ... >
  <mappingRules>
    <rule assembly="MySite.Model.Blog" schema="mysite_blog" />
    <rule assembly="MySite.Model.Forum" schema="mysite_forum" />
  </mappingRules>
</domain>


The above example splits model into two schemas. One for persistent types related to blog module. The other one is for forum module. The rest is mapped to 'mysite' schema. 

Fluent configuration

As alternative to XML-based configuration you can programmatically define mapping in code. Mapping rules could be added by using DomainConfiguration.MappingRules collection. Two examples from previous section will look like this:

domainConfiguration.DefaultSchema = "myapp";
var rules = domainConfiguration.MappingRules;
rules.Map("MyApp.Model.Foo").ToSchema("myapp_foo");

domainConfiguration.DefaultSchema = "mysite";
var rules = domainConfiguration.MappingRules;
blogAssembly = typeof (MySite.Model.Blog.Post).Assembly;
forumAssembly = typeof (MySite.Model.Forum.Thread).Assembly;
rules.Map(blogAssembly).ToSchema("mysite_blog");
rules.Map(forumAssembly).ToSchema("mysite_forum");

The last example uses hypothetical 'MySite.Model.Blog.Post' and 'MySite.Model.Forum.Thread' types to discover the corresponding assemblies.

To be continiued

This post covered basic ideas as well as mapping to multiple schemas. The second part will cover mapping to multiple databases and related application design techniques. Stay tuned.

Friday, November 18, 2011

Precise control over indexes

This is a detailed description of the index-related features from the upcoming release mentioned in the previous post.

Database indexes were invented to improve the speed of data retrieval operations on a database table. But as always, they have the cost: slower writes and increased storage space. So database architects have to keep the balance between faster data retrieval and slower writes. Ideally, indexes should be applied only on columns that are used in filtering, ordering, etc.

1. Control over indexes on reference fields

Versions of DataObjects.Net prior to the upcoming one were pretty straightforward and applied indexes on every reference property, for example, in this model index would be placed on Owner property automatically.

public class Penguin : Entity {

  [Field]
  public Person Owner { get; set; }
}

DataObjects.Net behaved so in assumption that such indexes would boost performance in queries like:
var PoppersPenguins = session.Query.All<Penguin>().Where(a => a.Owner == MrPopper);

But what if the application doesn't have such queries? And the index that is created and maintained by the database is absolutely useless, moreover, it makes the performance worse? The upcoming version of DataObjects.Net has the answers. Here is what you can do to prevent automatic index creation on a reference field:

public class Penguin : Entity {

  [Field(Indexed = false)]
  public Person Owner { get; set; }
}

2. Control over index clustering

There are 2 main approaches in physical organization of data tables: unordered and ordered. The first one means that the records are stored in the order they are inserted, the second implies that the records are pre-sorted before storing. Both of them have advantages and disadvantages, depending on structure of primary key, usage scenarios, etc. Some database servers supports both cases, others - only one. Microsoft SQL Server supports both: unordered approach is a "heap table (file)" and ordered one is based on "clustered index" when data rows are physically sorted according to position of primary key in internal index structures (B+ tree).

For Microsoft SQL Server DataObjects.Net always chose "clustered index" as an organization of physical storage because this is quite efficient in most cases with only few exceptions: when your primary key is not integer auto-incremented data type but uniqueidentifier or char/varchar, clustered indexes become less efficient on data inserts. Until now there was no way to say that there is no necessity to make primary index clustered, but that has changed: DataObjects.Net provides easy way to control clustering of indexes. Here is how:

// Penguin table should not be clustered
[HierarchyRoot(Clustered = false)]
public class Penguin : Entity {

  [Field]
  public Person Owner { get; set; }
}

Note, removing clustering from hierarchy root you remove it for all its descendants.

Additional feature is the ability to define an index that is not primary and then use it for clustering, like this:

// Person table should be clustered by Name column
[HierarchyRoot]
[Index("Name", Clustered = true)]
public class Person : Entity {

  [Field]
  public string Name { get; set; }
}

This can be done for each class separately except the case when SingleTable inheritance scheme is used. Another obvious restriction is that there can be only one clustered index per table.

3. Support for partial (filtered) indexes

A partial index, also known as filtered index is an index which has some condition applied to it so that it includes a subset of rows in the table. This allows the index to remain small, even though the table may be rather large, and have extreme selectivity.

Our goal was not only to add the support, but to make it easy to use and prevent users from the necessity to write database server-dependent expressions in index definitions. Therefore, we decided to implement the following technique that gives us compile-time validation, type-safety and the power of standard .NET expressions:

[HierarchyRoot
[Index("Owner", Filter = "OwnerIndex")]
public class Penguin : Entity {

    public static Expression<Func<Penguin, bool>> OwnerIndex()
    {
      return p => p.Owner != null;
    }

    [Field]
    public Person Owner { get; set; } 
}

Note that depending on inheritance scheme applied, partial indexes can or can't contain expressions with columns defined in ancestors/descendants.

In case you want to move filter expression to a separate class, you should use the following construct:

[HierarchyRoot]
[Index("Owner", Filter = "PenguinOwnerIndex", FilterType = typeof(FilterExpressions))]
public class Penguin : Entity {

    [Field]
    public Person Owner { get; set; } 
}

public static class FilterExpressions {

    public static Expression<Func<Penguin, bool>> PenguinOwnerIndex()
    {
      return p => p.Owner != null;
    }
}

Partial indexes are supported in Microsoft SQL Server and PostgreSQL providers.

In the next post I'll describe other features of the upcoming release, stay tuned. In the meantime, we are preparing the new builds, installers and stuff.

Thursday, September 30, 2010

New article: "Atomicity of visible state change in complex action sequences in DataObjects.Net"

I just finished writing pretty large document: "Atomicity of visible state change in complex action sequences in DataObjects.Net"

The article describes one of important features of event notification system in DataObjects.Net, that is used in during synchronization of paired (inverse) associations and entity removals.

Any comments, error reports and suggestions are welcome. Russian-speaking developers can read the article in Russian.

The article (in IFrame):

Saturday, March 06, 2010

P.S. Guys, sorry, but I just decided to share your Skype pics here - people should know your real nature :)

// Actually I did this just to add some colors. And I know they don't read comments.

Friday, June 12, 2009

Wiki update

We're planning to restructure & update wiki.dataobjects.net on the next week. To find necessary topics, please use search, since many of them will be temporarily orphaned.

Monday, June 01, 2009

Our Wiki got links to class reference

Check out this feature here.

Note: our online help is working on beta version of new Help Server, so there are some issues, that will be resolved on this week.