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.

Reply via email to