FluentNHibernate: 1.3.0.733
NHibernate: 3.3.1.4000
I'm trying to set the column name of the Id column, but it seems to be ignored.
Edit:
Found solution. Property redeclaration (new-modifier) was the problem (see answer).
I'm using AutoMapping with conventions and overrides.
Override:
public class OrderHeadMapping : IAutoMappingOverride<OrderHead>
{
public void Override(AutoMapping<OrderHead> mapping)
{
mapping.Schema("[database].[dbo]");
mapping.Table("OrderHeads");
mapping.Id(x => x.Id, "desiredColumnName")
.Column("desiredColumnName")
.GeneratedBy.UuidString();
...
}
}
This code gets executed, but the column name stays "Id".
I've already exported the mappings to an directory to see what's the outcome:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class xmlns="urn:nhibernate-mapping-2.2" schema="[database].[dbo]" name="OrderHead, Core, Version=1.0.4666.19686, Culture=neutral, PublicKeyToken=null" table="OrderHeads">
<cache usage="read-write" />
<id name="Id" type="System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="Id" />
<generator class="assigned" />
</id>
...
</class>
</hibernate-mapping>
I've searched my whole solution for ".Id(" and ".Column(" to ensure it is not accidently reset but none of the results deal with setting/overwriting the id column name. So now I'm a little bit lost.
I finally found the problem:
One thing I left out in my description is that I have two base entities:
public abstract class Entity
{
protected object _id;
public virtual object Id
{
get { return _id; }
protected internal set { _id = value; }
}
}
public abstract class Entity<TKey> : Entity
{
public Entity()
{
_id = default(TKey);
}
new public virtual TKey Id
{
get { return (TKey)_id; }
protected internal set { _id = (TKey)value; }
}
...
}
The problem was that FluentNHibernate handles redefined properties twice: Entity.Id and Entity<>.Id ultimately overwriting the desired mapping with the mapping of the base class version. So I have to walk the inheritance tree up to check if this member is the top most rewrite (if any).
Now I handle the problem in the ShouldMap
method in my implementation of DefaultAutomappingConfiguration
:
public override bool ShouldMap(Member member)
{
var res = base.ShouldMap(member);
if (res == true)
{
var originalDeclaringType = GetOriginalDeclaringType(member.MemberInfo);
...
if(member.Name == "Id")
{
if (GetTopMostRedefinedMember(member.MemberInfo) != member.MemberInfo.DeclaringType)
return false;
}
}
...
return res;
}
with GetTopMostRedefinedMember being:
private Type GetTopMostRedefinedMember(MemberInfo member)
{
List<Type> types = new List<Type>();
Type type = member.ReflectedType;
while (type != null)
{
types.Add(type);
type = type.BaseType;
}
foreach (var t in types)
{
var tmp = t.GetMember(member.Name, BindingFlags.Public |
BindingFlags.NonPublic |
BindingFlags.Instance |
BindingFlags.DeclaredOnly);
if (tmp.Length != 0)
{
type = t;
break;
}
}
return type;
}
Disclaimer: This code is not thoroughly tested.
I hope someday someone will be safed from debugging hours!
Lg
warappa