Assume I have the following two classes:
public class User : Entity
{
public virtual IList<Item> Items { get; set; }
}
public class Item : Entity
{
public virtual User Owner { get; set; }
}
I created two mapping classes:
public class UserMap : ClassMap<User>
{
public UserMap()
{
Id(x => x.Id);
HasMany(x => x.Items);
}
}
public class ItemMap : ClassMap<Item>
{
public ItemMap()
{
Id(x => x.Id);
References(x => x.Owner);
}
}
This will result in a table Item
that has a column UserId
and a column OwnerId
. When I use KeyColumn("OwnerId")
on the HasMany
mapping, it works with only the OwnerId
column, but I would like to avoid that. Is there a way to tell NHibernate, to use the column created by the mapping in ItemMap
?
Why I want to avoid specifying the column explicitly:
The column name OwnerId
is automatically being generated based on the name of the property and some rules. If I change either the rules or the property name, I need to remember to change that KeyColumn
, too. So, basically, it is not refactoring save.
Updated: fixed bug
if you apply the rules in the maps then
public static void KeyColumnFromReference<TChild>(
this OneToManyPart<TChild> collectionmap, ClassMap<TChild> map, Expression<Func<TChild, object>> referenceprop)
{
string propertyname = GetPropertyName(referenceprop);
var column = ((IMappingProvider)map).GetClassMapping()
.References.First(m => m.Name == propertyname)
.Columns.First().Name;
collectionmap.KeyColumn(column);
}
public static void KeyColumnFromReference<T, TChild>(
this OneToManyPart<TChild> collectionmap, ClassMap<TChild> map)
{
var column = ((IMappingProvider)map).GetClassMapping()
.References.First(m => m.Type == typeof(TChild))
.Columns.First().Name;
collectionmap.KeyColumn(column);
}
public UserMap()
{
HasMany(x => x.Items)
.KeyColumnFromReference<User, Item>(new ItemMap());
// or
HasMany(x => x.Items)
.KeyColumnFromReference(new ItemMap(), u => u.Owner);
}
if you apply the rules as conventions then you need to implement IHasManyConvention
and apply the same rules on the EntityType
and propertyname (which you have to get through reflection from the ChildType)
Update:
class ForeignKeyConvention : IHasManyConvention
{
public void Apply(IOneToManyCollectionInstance instance)
{
// to force the compiler to take the Name property and not the Name method
string propertyName = ((ICollectionInspector)instance).Name;
// should be equal to the convention for the reference key column
instance.Key.Column(propertyName + instance.EntityType.Name + "id");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With