Search code examples
c#nhibernatenhibernate-mapping-by-code.net-4.6

NHibernate mapping-by-code in .NET 4.6 throws "Cannot extend unmapped class"


I have project running for several months in same NH configuration that I opened today on a clean installation of Windows (VS 2015 RC is installed, also .NET 4.6 RC is installed, but project is targeting .NET 4.5)

In building of NH session factory when these lines are called:

var mapper = new AutoMapper();
var mapping = mapper.CompileMappingFor(typeof(Entity).Assembly.GetTypes());
conf.AddMapping(mapping);

the last row throws an exception: Could not compile the mapping document: mapping_by_code with InnerException Cannot extend unmapped class: [my_namespace].[my_entity_name]. When I iterate through mapping, I can see there object with name [my_entity_name] and everything seems ok. Just this object is in collection after the objects that extends this object (I'm not sure if it can be a problem or the order doesn't matter).

On any other computer (other workstation, CI server, production and dev environment) we didn't notice this error, I suppose that it can be caused by installed .NET 4.6, this is the only machine with .NET 4.6. If I understand it correctly when .NET 4.6 is installed all projects targeting .NET 4.5 are running in .NET 4.6 runtime. I also tried to run it in VS 2013, but with no change... I was also checking .NET 4.6RC changelog but I didn't find anything that could cause this error. Any suggestions?


UPDATE: I checked on .NET 4.5 machine the order of items in mapping collection and it is in correct order (dependent subclass is after the parent subclass), so it seems that the order of types returned by GetTypes() (no ordering is guaranteed) is coincidentally working in .NET 4.5, but not working in .NET 4.6 because CompileMappingFor does not reorder mappings of same type (here I have dependency SubClass -> SubClass).


Solution

  • Yes, the reason was that the order of Assembly.GetTypes() is completely different in .NET 4.5 and .NET 4.6. It was a coincidence that order produced by .NET 4.5 was working... I modified it to following piece of code and it works.

    var types = typeof (Entity).Assembly.GetTypes().Where(t => !t.IsInterface).PartialOrderBy(x => x, new EntityTypeComparer());
    var mapping = mapper.CompileMappingFor(types);    
    
    public class EntityTypeComparer : IComparer<Type>
    {
        public int Compare(Type x, Type y)
        {           
            if (x == y)
                return 0;
            if (x.IsAssignableFrom(y) || (!x.IsAssignableTo<Entity>() && y.IsAssignableTo<Entity>()))
                return -1;
            if (y.IsAssignableFrom(x) || (!y.IsAssignableTo<Entity>() && x.IsAssignableTo<Entity>()))
                return 1;
            return 0;
        }
    }      
    

    EDIT: OrderBy replaced with PartialOrderBy, you can find implementation here - Topological Sorting using LINQ. This makes topological sort instead of regular sort - we have there objects that are not comparable (last return 0 in the comparer) and general OrderBy can produce wrong results.