Monday, October 26, 2009

Arbitrary keys & hierarchies, part 7. Custom key generators

As DataObjects.Net v4 supports wide variety of keys but has default implementation only for key generators with exactly one identity field, there could be scenario in which custom key generator is required. To close the gap DataObjects.Net v4 declares the following abstract class:

public abstract class KeyGenerator
{
  public KeyProviderInfo KeyProviderInfo { get; private set; }

  public abstract Tuple Next();

  public virtual void Initialize() {}

  protected KeyGenerator(KeyProviderInfo keyProviderInfo) {}
}

Custom generator type must inherit KeyGenerator type and implement at least the abstract method KeyGenerator.Next(). All necessary information concerning the structure of key, caching behavior and so on can be found in KeyProviderInfo type. Here is the custom implementation of key generator for Guid type:

public sealed class MyGuidKeyGenerator : KeyGenerator
{
  public override Tuple Next()
  {
    return Tuple.Create(KeyProviderInfo.TupleDescriptor, Guid.NewGuid());
  }

  public MyGuidKeyGenerator(KeyProviderInfo keyProviderInfo)
    : base(keyProviderInfo)
  {}
}

In order to indicate that a hierarchy must be served with custom key generator, KeyGeneratorAttribute was introduced. Here is how it is intended to be  used:

[HierarchyRoot]
[KeyGenerator(typeof(MyGuidKeyGenerator))]
public class Author : Entity
{
  [Field, Key]
  public Guid Id { get; private set; }

  [Field]
  public EntitySet<Book> Books { get; private set; }
}

In scenarios when key generator for a hierarchy is not required at all, this must be set up appropriately:

[HierarchyRoot]
[KeyGenerator(KeyGeneratorKind.None)]
[TableMapping("Metadata.Type")]
[Index("Name", Unique = true)]
public class Type : MetadataBase
{
  [Field, Key]
  public int Id { get; private set; }

  [Field(Length = 1000)]
  public string Name { get; set; }

  public Type(int id, string name) 
    : base(id)
  {
    Name = name;
  }

Here is how system class Metadata.Type is declared in DataObjects.Net v4. Pay attention to KeyGeneratorAttribute usage together with the absence on parameterless constructor. This means that identity field values are provided from the outside and there is actually no need in key generator.

This is the last post in “Arbitrary keys & hierarchies” series. Hope you’'ll find it useful.

Part 6. Identity fields

P.S.
If you want me to blog on some particular topic concerning DataObjects.Net v4 domain – make a request in comments.

CodeProject

4 comments:

  1. Thank you for your excellent article about the keys. It would like to read about the domain model DataObjects.Net.

    ReplyDelete
  2. Hello polygris,
    Thanks for your interest in DataObjects.Net!

    If you want some detailed information on Domain Model then let it be so. After a short break I'll start blogging on it.

    ReplyDelete
  3. Hi Dmitri.

    Brilliant Blog!!!
    O've got an existing database and want to use DO4 with it... all of the primary keys are set up on SQL Server as identity() keys.

    What attributes / key generators do I use to tell DO4 to use SQL server's identity function?

    ReplyDelete
  4. Hello maranite2,

    Thanks for your comment.

    Firstly, I should say that existing database support is still under development. That main problem is in TypeId column which presence in all tables is expected by current version of DataObjects.Net. We are going to remove this requirement in future versions. At least, for hierarchies with Concrete Table mapping scheme it is meaningless at all.

    Secondly, in order to map key generator to identity() column in existing table you should create your own key generator class for each hierarchy, say ProductGenerator, CustomerGenerator an so on. Each of them must inherit KeyGenerator class and implement the "Next()" abstract method with the following logic:
    1. open session & transaction
    2. execute this SQL batch
    INSERT empty row into the specified table;
    SELECT IDENTITY_SCOPE;
    3. read resulting value
    4. rollback transaction & close session
    5. construct tuple with value from #3 and return it.

    If you face any difficulties, send me a message or visit our forum at x-tensive.com.

    ReplyDelete