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>
Thank you! Precisely what I was looking for!
ReplyDelete