Sunday, March 11, 2012

Mapping-by-code and custom ID generator class

In the comments to one of my mapping-by-code posts Cod asked if it is possible to specify a custom ID generator class within mapping-by-code mappings. I didn't know the answer but the topic seems to be interesting enough to figure it out.

The answer is of course positive - mapping-by-code API is flexible enough to support that. Let's remind how we normally specify the generator class to be used:

Id(x => x.Id, m =>
{
m.Generator(Generators.Native, g => g.Params(new
{
// generator-specific options
}));
});

The Generator method's first parameter expects an instance of IGeneratorDef class. NHibernate provides a set of predefined ones in Generators static class - see the full list here - but we may provide our own implementation as well.

Let's hook up a custom generator class as implemented in this NHForge's article. FDPSequence class defined there is an integer-based, parametrized generator (implementation of NHibernate's IIdentifierGenerator). To use it within mapping-by-code, we need to prepare IGeneratorDef class accordingly. But that's pretty easy:

public class FDPSequenceDef : IGeneratorDef
{
public string Class
{
get { return typeof(FDPSequence).AssemblyQualifiedName; }
}

public object Params
{
get { return null; }
}

public Type DefaultReturnType
{
get { return typeof(int); }
}

public bool SupportedAsCollectionElementId
{
get { return true; }
}
}

We have to implement 4 properties:

  • Class is an equivalent of class attribute in XML - this is the place where we need to specify our custom generator assembly qualified name.
  • Params allows us to create non-standard <param> elements equivalents. We could return an anonymous object with values set i.e through the constructor - but I don't think it is needed as we can always pass parameters through the second Generator method's parameter, as an anonymous object, too.
  • DefaultReturnType specifies what is the type generated by our custom generator (may be null, NHibernate will figure it out through the reflection later)
  • and SupportedAsCollectionElementId obviously specifies if our generator is usable within collection elements.

Having FDPSequenceDef in place, we just need to pass it to Generator method in mapping-by-code:

Id(x => x.Id, m => m.Generator(new FDPSequenceDef()));

And we're done! The XML generated looks like expected and the generator is working for us:

<id name="Id" type="Int32">
<generator class="NHWorkshop.FDPSequence, NHWorkshop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</id>

1 comment:

  1. Thank you! Precisely what I was looking for!

    ReplyDelete