Thursday, February 2, 2012

Mapping-by-Code - Any

Probably the last relation type supported by NHibernate we have left is any relationship. It is quite an exotic feature. Let's re-use Ayende's model again:

Any lets us to map many-to-one relationship that has entities of different types on the "one" side. There's no foreign keys defined on the database side and the relation logic is maintained on the object level only. To make it work, we need to tell NHibernate what types of entities we expect and how to distinguish it. Here's how to do it in mapping-by-code, with all the options available:

Any(x => x.Payment, typeof(long), m =>
{
m.IdType<long>();
m.MetaType<string>();
m.MetaValue("CreditCard", typeof(CreditCardPayment));
m.MetaValue("Wire", typeof(WirePayment));

m.Columns(id =>
{
id.Name("PaymentId");
id.NotNullable(true);
// etc...
}, classRef =>
{
classRef.Name("PaymentType");
classRef.NotNullable(true);
// etc...
});

m.Access(Accessor.Field);
m.Lazy(true);
m.Cascade(Cascade.Persist);
m.Index("payment_idx");
m.Insert(true);
m.Update(true);
m.OptimisticLock(true);
});

The first parameter is - as always - a lambda pointing to the mapped property. The second defines the common type for identifiers of all entities at the other side. Third one is for mapping options, which are pretty similiar to already mentioned ManyToAny relationship.

Inside the configuration we need to define properties for two columns - one to keep the other entity identifier, second to keep its discriminating value. We do it using Columns method's parameters. Later we have to specify the type of discriminator using MetaType method and its generic argument - string is good here. We can also specify the common type of identifiers using IdType method, but we've already did it in Any second parameter (I think that this method is useless here). The last thing we need to do is to define the list of entity types that are allowed at other side of the relation and its corresponding discriminator values. In order to do this, we call MetaValue method - its first parameter is the discriminator value, second is the type. There are also several standard options available, like Lazy and Cascade.

Fluent NHibernate's equivalent

Contrary to many-to-any, any is supported well in Fluent NHibernate.

ReferencesAny(x => x.Test)
.IdentityType<long>()
.MetaType<string>()
.AddMetaValue<CreditCardPayment>("CreditCard")
.AddMetaValue<WirePayment>("Wire")
.EntityIdentifierColumn("PaymentId")
.EntityTypeColumn("PaymentType")
.Access.Field()
.LazyLoad()
.Cascade.All()
.Insert()
.Update()
.OptimisticLock();

There is no Index method and we can't set options other than name for database columns. Apart from that, the rest is pretty clear and easy.


The next post in Ayende's series was about many-to-any, but I've covered it within collections and relation types, so I'm going to skip it.

2 comments:

  1. How would look the QueryOver with filter. Lets say QueryOver().Where(x => x.Test.PaymentId==someId)? Thank you

    ReplyDelete
    Replies
    1. I don't think that querying over "any" relationship is possible in NHibernate. Well, SQL needed to do such a query would be pretty complex, probably joining as many tables as possible types. You should better look for another approach.

      Delete