Friday, January 13, 2012

Mapping-by-Code - ManyToOne

Next up for inspection for mapping-by-code is the <many-to-one> element. It's mapping-by-code equivalent is ManyToOne. Here are its possible options:

ManyToOne(x => x.PropertyName, m =>
{
m.Column("column_name");
// or...
m.Column(c =>
{
c.Name("column_name");
// other standard column options
});

m.Class(typeof(ClassName));
m.Cascade(Cascade.All | Cascade.None | Cascade.Persist | Cascade.Remove);
m.Fetch(FetchKind.Join); // or FetchKind.Select
m.Update(true);
m.Insert(true);
m.Access(Accessor.Field);
m.Unique(true);
m.OptimisticLock(true);

m.Lazy(LazyRelation.Proxy);

//m.PropertyRef ???
//m.NotFound ???

m.ForeignKey("column_fk");
m.Formula("arbitrary SQL expression");
m.Index("column_idx");
m.NotNullable(true);
m.UniqueKey("column_uniq");
});

The first parameter (as almost always) is an lambda expression specifying mapped property in our entity. The second (as almost always) are the mapping options.

Column method (as almost always, again) is to specify database column that keeps the relation value. By default, its name is equal to property name, without any "_id" postfix or something. We can change its name only using the overload with string or we can customize all the options using the second overload. Its possibilities are shown in the post about property mapping - it generally works the same way, so I'll skip it here.

Cascade is used pretty often. Note that mapping-by-code redefined it a bit: save-update option is called Persist and delete is Remove here. This is configured using an flags-type enumerable, so it's easy to combine values. There's also an alternative syntax provided using extension methods:

    m.Cascade(Cascade.Persist.Include(Cascade.Remove));

By now, there's no way to map two features useful for legacy inconsistent databases - property-ref and not-found. Maybe Fabio decided that legacy databases wouldn't be mapped with new mappings?

Fluent NHibernate's equivalent

ManyToOne's equivalent in FNH is References:

References(x => x.PropertyName, "column_name")
.Class<ClassName>()
.Cascade.SaveUpdate()
.Fetch.Join()
.Update()
.Insert()
.ReadOnly()
.Access.Field()
.Unique()
.OptimisticLock()
.LazyLoad(Laziness.Proxy)
.PropertyRef(x => x.PropertyRef)
.NotFound.Ignore()
.ForeignKey("column_fk")
.Formula("arbitrary SQL expression")
.Index("column_idx")
.Not.Nullable()
.UniqueKey("column_uniq");

ReadOnly is just a FNH's shortcut for setting both .Not.Insert() and .Not.Update().

9 comments:

  1. Are there any resources that have real world examples of how to use mapping by code?

    ReplyDelete
  2. Also, I do not understand why ManyToOne requires a property. The xml mappings do not specify one. Maybe (probably) I am just slow at this time of day!

    Thanks.

    ReplyDelete
    Replies
    1. Well, it needs either property (name in XML) or formula. See Ayende's great post.

      Delete
  3. Is it possible to sue System.Guid with ManyToOne? I've tried this line of code in my mapping

    ManyToOne(x => x.ApplicationId, map => {map.Column("ApplicationId"); map.NotNullable(true); map.Cascade(Cascade.None); })

    I get "'System.Guid' must be a reference type in order to use it as
    parameter 'TProperty'"

    Where the ApplicationId is a System.Guid. The data structure is the aspnet mebership tables.

    Is this expected behavior from nhibernate?

    ReplyDelete
    Replies
    1. Yes, this is not the mapping-by-code or Guid issue. You should reference Application class in your entity, not the foreign key id (Guid), which should not be exposed in your model classes. This is how all relationships in NHibernate (and in fact most ORMs) work.

      Delete
  4. Hi every one,
    I use Nhibernate ByCode, and need to map a ManyToOne with a formula, but this feature seems not to be supported. Someone has a solution?

    Thanks in advance

    ReplyDelete
    Replies
    1. It should be supported, it is available in ManyToOne options. What have you tried?

      Delete
  5. Is property-ref supported now?

    ReplyDelete