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

Thursday, October 11, 2012

DataObjects.Net Extensions update

DataObjects.Net Extensions for DataObjects.Net 4.5.5 and 4.6.0 are available.

Changes

  •     Added missing xmldocs for BulkOperations extension
  •     New Tracking extension (DataObjects.Net Extensions 4.6.0 only)

Tracking extension

Tracking extension provides tracking/auditing functionality on Session/Domain level.

To use it:

1. Add reference to Xtensive.Orm.Tracking assembly

2. Include types from Xtensive.Orm.Tracking assembly into the domain:
<Xtensive.Orm>
  <domains>
    <domain ... >
      <types>
        <add assembly="your assembly"/>
        <add assembly="Xtensive.Orm.Tracking"/>
      </types>
    </domain>
  </domains>
</Xtensive.Orm>

3. To track changes on Session level obtain an instance of ISessionTrackingMonitor through Session.Services.Get<ISessionTrackingMonitor>() method To track changes on Domain level (from all sessions) obtain an instance of IDomainTrackingMonitor through Domain.Services.Get<IDomainTrackingMonitor>() method

4. Subscribe to TrackingCompleted event. After each tracked transaction is committed you receive the TrackingCompletedEventArgs object.

5. TrackingCompletedEventArgs.Changes contains a collection of ITrackingItem objects, each of them represents a set of changes that occured to an Entity within the transaction committed.

Example

1. Subscribe to ISessionTrackingMonitor/IDomainTrackingMonitor TrackingCompleted event
var monitor = Domain.Services.Get<IDomainTrackingMonitor>();
monitor.TrackingCompleted += TrackingCompletedListener;

2. Do some changes to persistent entities
using (var session = Domain.OpenSession()) {
  using (var t = session.OpenTransaction()) {
    var e = new MyEntity(session);
    e.Text = "some text";
    t.Complete();
  }
}

3. Handle TrackingCompleted event call and do whatever you want with tracked changes.
private void TrackingCompleted(object sender, TrackingCompletedEventArgs e)
{
  foreach (var change in e.Changes) {
    Console.WriteLine(change.Key);
    Console.WriteLine(change.State);

    foreach (var value in change.ChangedValues) {
      Console.WriteLine(value.Field.Name);
      Console.WriteLine(value.OriginalValue);

      Console.WriteLine(value.NewValue);
    }
  }
}

Sync extension

Sync extension was planned for 4.6 release. We decided that it is not production ready and postponed it until 4.7.

Download

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.

Monday, October 08, 2012

Issues with recent PostSharp

Recent versions of PostSharp (2.1.7.17 and 2.1.7.18) does not work well with DataObjects.Net. If you installed DataObjects.Net from NuGet and get the following error when compiling:

Error in MulticastAttributeUsage applied on type 'Xtensive.Orm.PersistentAspect': attribute inheritance is not supported on properties and events.

you should downgrade your PostSharp to version 2.1.7.16.

To do this open package manager console (Tools ->Library package manager -> Package Manager Console) and execute the following commands:

> Uninstall-Package PostSharp -Force
> Install-Package PostSharp -Version 2.1.7.16

Recently released DataObjects.Net 4.5.5 and 4.6.0 explicitly set maximal supported version of PostSharp to 2.1.7.16 so you will not get this error when using them.


DataObjects.Net 4.6 goes final

I'm glad to announce that DataObjects.Net 4.6.0 is released. This post provides an overview of changes and improvements in 4.6 version. They would be explained with more details in future posts.

SQLite provider

SQLite has joined the list of supported databases.

Quick domain configuration example:

    <domain name="mySqliteDomain"
            providerName="sqlite"
            connectionString="Data Source=mydatabase.db" />


You'll also need to add reference to Xtensive.Orm.Sqlite.dll assembly in your project.

Mapping to multiple schemas and even databases

Since 4.6 it's possible to map persistent model to multiple schemas and databases.

The following examples maps model to two schemas based on entity namespaces:

    <domain name="myDomainWithTwoSchemas" defaultSchema="dbo">
        <mappingRules>
            <rule namespace="MyApp.Model.Foo" schema="foo" />
            <rule namespace="MyApp.Model.Bar" schema="bar" />
        </mappingRules>
    </domain>


I'll cover multi-mapping configurations in future posts.

Upgrade improvements

In this version we heavily reworked upgrade to be more fast and reliable. First major improvement is extensive usage of parallel computations, so now DataObjects.Net will perform some actions in parallel to build domain faster. Second major improvement is wrapping all upgrade actions in a single transaction if database supports transactional DDL. Previous versions of DataObjects.Net used two transactions in Perform and PerformSafely modes because additional upgrade domain was created.

Simplified key generator API

In DataObjects.Net 4.6 we changed API for key generators (both standard and user-written) to be more simple and intuitive. For example for creating custom key generator table or sequence you no longer need to write domain service. Simple [KeyGenerator(Name = "Foo")] will work just well. I'll explain new key generator API in details in future posts.

Final words

Just as always you can download packages from our site or NuGet gallery.

DataObjects.Net 4.5.5 released

DataObjects.Net 4.5.5 is made available. This is bug-fixing release for 4.5 branch. List of resolved problems could be found here. Just as always you can download packages from our site or NuGet gallery.

Monday, October 01, 2012

DataObjects.Net 4.5.5 RC and 4.6.0 RC

Two release candidates for 4.5 and 4.6 branches are made available.

Here is short list of changes:

DataObjects.Net 4.6.0 RC

Added NamingRules.RemoveDots and NamingRules.RemoveHyphens

New options work similar to existing UnderscoreDots and UnderscoreHyphens except that dots and hyphens are completely removed from table or column name instead of replacing with underscore.

Added Connection and Transaction properties to UpgradeContext

DataObjects.Net provides single transaction for upgrade process. Those properties allows to execute custom actions at any stage.

Added Session property to UpgradeContext

This is new recommended approach for getting current session in upgrade handlers.

UpgradeStage.Initializing is marked obsolete

This stage no longer occurs in DataObjects.Net 4.6 so corresponding warning would be emitted when it is used.

Added OnPrepare and OnComplete methods to UpgradeHandler which are executed before and after any upgrade

OnPrepare partially replaces old Initializing stage. It is executed are very early stage so you can apply fixes to the database schema before any upgrade actions are performed.
OnComplete is executed at the very last stage and could be used for configuring domain before it is returned from Domain.Build() method.

Added advanced version of domain builder module

New method OnAutoGenericBuilt() allows to change created automatic generic instances.

Correctly clean up key generator tables if entities are created in OnUpgrade() method

If entities are created in OnUpgrade() method key generator tables (i.e. Int64-Generator) might be not empty after the upgrade. This is fixed now.

Several methods that implicitly use Session are marked as obsolete

Since DataObjects.Net 4.4 we are recommending explicitly pass session everywhere.
Some old methods that implicilty use Session.Current has been marked as obsolete.
Examples of such methods InvokeTransactionally() extension method overloads that don't take session and GetTypeInfo() extenstion method.

DataObjects.Net 4.5.5 RC

Omit redundant parentheses in generated SQL for chains of set operations

This allows to write rather large chains of .Union() calls.
See this bug report for details.

Correctly handle hierarchy split when doing upgrade

This fixes error about TypeId removals when hierarchy has been split to hierarchies with single type.

Fixed translation of Contains/Any/All under certain scenarios

See this bug report for details.

Correctly parse pre-release versions of MySQL (e.g. 5.5.25a)

See this bug report for details.

Other changes

Debug symbols (.pdb) are available for this and all future release. They require source code license
just like source code package.

Final words

Just as always distribution packages are available on our download site and NuGet gallery.