I'm currently trying to migrate from NH 2.1 with FNH 1.0 to NH 3.2 with its
built-in Mapping By-Code facilities. So far, this worked pretty well, but
I'm having one particular problem with classes that contain collections
which should be mapped to different tables in derived classes.
The base stuff looks something like this (code might contain typos):
public abstract class LocalizedEntity<TLoc> where TLoc : LocBase
{
public Guid Id { get; private set; } //other common base properties
omitted
public ICollection<TLoc> Localizations { get; private set; }
}
public abstract class LocBase
{
public string LanguageCode { get; set; }
}
public abstract class LocalizedEntityMap<T, TLoc> : ClassMapping<T> where T
: LocalizedEntity<TLoc> where TLoc : LocBase
{
protected LocalizedEntityMap() : this(null) { }
protected LocalizedEntityMap(string tableName)
{
Id(p => p.Id);
//[...]
Bag(p => p.Localizations,
m =>
{
//some common collection parameters
if (!string.IsNullOrEmpty(tableName))
m.Table(tableName);
},
m => m.Component(c =>
{
c.Property(p => p.LanguageCode);
MapLocalization(c);
}));
}
protected abstract void MapLocalization(IComponentElementMapper<TLoc> c);
}
And then we have a lot of classes that define their own TLoc (since they
are special):
public class SpecialEntity : LocalizedEntity<SpecialLoc> { }
public class SpecialLoc : LocBase { }
They work just fine, because every TLoc is different.
Then we have hundreds of other classes that share the same TLoc (same set
of properties, like name):
public class CommonLoc : LocBase
{
public string Name { get; set; }
}
public class SomeCommonEntity : LocalizedEntity<CommonLoc> { }
public class SomeCommonMap : LocalizedEntityMap<SomeCommonEntity>
{
public SomeCommonMap()
{
//use default conventions
}
}
But then we have some special cases (four in total) that need their own
table names specified (some are legacy and cannot be changed to match the
convention, others are simply too long to work on certain DBMS which have
limits on how long a table name can be):
public class SomeLongNameLegacyEntity : LocalizedEntity<CommonLoc> { }
public class SomeLongNameLegacyMap :
LocalizedEntityMap<SomeLongNameLegacyEntity>
{
public SomeLongNameLegacyMap()
: base("ShortLegacy_Loc")
{
Table("ShortLegacy");
}
}
Conventions are applied using ModelMapper.BeforeMapXxx:
mapper.BeforeMapBag += (mi, m, pc) =>
pc.Table(m.GetRootMember().ReflectedType.Name + "_Loc");
mapper.BeforeMapClass += (mi, t, pc) => pc.Table(t.Name);
Now once the mapping is compiled, *every *Entity using CommonLoc is mapped
to the *last *table that was mapped with the LocalizedEntityBase(string)
ctor; ie. for SomeCommonEntity the table ShortLegacy_Loc (as specified by
SomeLongNameLegacyMap) is used instead of SomeCommonEntity_Loc (as per
convention)! Those (four entities) explicitly specifying a table name for
the Bag work correctly.
I traced this back to PropertyContainerCustomizer.RegisterBagMapping where
both the DeclaringType member (LocalizedEntity<CommonLoc>.Localizations)
and the ReflectedType member (SomeCommonEntity.Localizations and
SomeLongNameLegacyEntity.Localizations) are passed on.
Removing the DeclaringType one breaks one of the MappingByCode tests
(AllPropertiesRegistrationTests.WhenMapPropertiesInTheInheritedThenMapInBase)
which verifies that such cases are mapped according to the ORM rules (by
mapping them into the base class).
Adding a check whether DeclaringType is a generic (or abstract) type does
work, but I'm a bit hesitant to just leave it like that; alas I didn't know
about those "ORM rules" until I saw the breaking test, so I don't exactly
know if its a good fix or not.
The test has an interresting comment that kinda appeals to me:
Where needed, using the SimpleModelInspector, the user can revert this behavior
checking the DeclaringType and ReflectedType of the persistent member
But then again, my problem is about Components in Bags, not Entities or
RootEntities.
So, does anyone else have mappings similar to ours (that work), or a tip on
how to fix the problem other than editing the NH source?
I do have a workaround available (that is, setting the table name in
BeforeMapBag for those four entities that need it changed instead of the
abstract class mapping), but this can't be the right thing to do here.
--
You received this message because you are subscribed to the Google Groups
"nhusers" group.
To view this discussion on the web visit
https://groups.google.com/d/msg/nhusers/-/eZ0glHvbnTAJ.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/nhusers?hl=en.