Custom mapping overrides doesn't work in modules because of bug in FluentNHibernate

Topics: General, Writing modules
Apr 6, 2011 at 7:51 PM

I'm currently writing own startup using Orchard. I've noticed some problem with implementing my own custom fluenhibernate  conventions in module. After examining Orchard code I've found this in AbstractDataServicesProvider class:

public static AutoPersistenceModel CreatePersistenceModel(IEnumerable<RecordBlueprint> recordDescriptors) {
    return AutoMap.Source(new TypeSource(recordDescriptors))
        // Ensure that namespaces of types are never auto-imported, so that 
        // identical type names from different namespaces can be mapped without ambiguity
        .Conventions.Setup(x => x.Add(AutoImport.Never()))
        .Conventions.Add(new RecordTableNameConvention(recordDescriptors))
        .Alterations(alt => {
            foreach (var recordAssembly in recordDescriptors.Select(x => x.Type.Assembly).Distinct()) {
                alt.Add(new AutoMappingOverrideAlteration(recordAssembly));
            }
            alt.AddFromAssemblyOf<DataModule>();
            alt.Add(new ContentItemAlteration(recordDescriptors));
        })
        .Conventions.AddFromAssemblyOf<DataModule>();
}

Line with ".Alterations" is trying to get conventions overrides from all modules which contains at least one class which make them as RecordBlueprint containers.

Method from CompositionStrategy.cs ..

        private static bool IsRecord(Type type) {
            return ((type.Namespace ?? "").EndsWith(".Models") || (type.Namespace ?? "").EndsWith(".Records")) &&
                   type.GetProperty("Id") != null &&
                   (type.GetProperty("Id").GetAccessors() ?? Enumerable.Empty<MethodInfo>()).All(x => x.IsVirtual) &&
                   !type.IsSealed &&
                   !type.IsAbstract &&
                   (!typeof(IContent).IsAssignableFrom(type) || typeof(ContentPartRecord).IsAssignableFrom(type));
        }

decide if passed type is record for RecordBlueprint. So it is enough to have any class in module Model namespace which is delivered from ContentPartRecord.

I've created this class in my module

public class DummyRecord : ContentPartRecord { }

FluentNHibernate class AutoMappingOverrideAlteration looks for implementation of IAutoMappingOverride<T> in passed assembly. So I've created

    public class MappingAltertation : IAutoMappingOverride<DummyRecord>
    {
        /// <summary>
        /// Alter the automapping for this type
        /// </summary>
        /// <param name="mapping">Automapping dummy record.</param>
        public void Override(AutoMapping<DummyRecord> mapping)
        {
            throw new NotImplementedException("Mapping overriding works in module.");
        }
    }

Everything should work correctly, I mean that I've expected this "NonImplementedException" but it doesn't.

Why ? ...

Altertations from FluentNhibernate uses AutoMappingAlterationCollection.cs for collecting classes which implements IAutoMappingAlteration. This is the code:

        /// <summary>
        /// Adds an alteration
        /// </summary>
        /// <param name="alteration">Alteration to add</param>
        /// <returns>Container</returns>
        public AutoMappingAlterationCollection Add(IAutoMappingAlteration alteration)
        {
            if (!alterations.Exists(a => a.GetType() == alteration.GetType()))
                alterations.Add(alteration);
            return this;
        }
As you can see this code doesn't allow to add more than one type altertation class. This means that there can be added only one instance of AutoMappingOverrideAlteration class with no matter what assembly is passed in its constructor.

So the problem is that only first module in recordDescriptors.Select(x => x.Type.Assembly).Distinct()  will be added to altertations.

There is a fix for that in FluentNhibernate in commit from January 03 2011 https://github.com/jagregory/fluent-nhibernate/commit/3dd538a458c92476cd7b910656ba190dc0c6aea6

Which change Add method to this

        /// <summary>
        /// Adds an alteration
        /// </summary>
        /// <param name="alteration">Alteration to add</param>
        /// <returns>Container</returns>
        public AutoMappingAlterationCollection Add(IAutoMappingAlteration alteration)
        {
            if (!alterations.Exists(a => a.GetType() == alteration.GetType() && alteration.GetType() != typeof(AutoMappingOverrideAlteration)))
                alterations.Add(alteration);
            return this;
        }
With this fix everything should work

I've try to patch FluentNhibernate 1.0.0.0 and use it with Orchard but it failed because some method is missing ClasslikeMapBase<T>

public ManyToOnePart<TOther> References<TOther>(Type declaringType, string propertyName, string columnName);

Is FluentNhibernate library which Orchard uses is patched somehow ? Could you share this patch for FluentNhibernate if there is one ? 

I'm very missing my own custom mapping overrides in modules :)

 

 

 

Apr 29, 2011 at 3:08 PM
Edited Apr 29, 2011 at 3:10 PM

Hi,

Experiencing the same problem. Good explanation.

Would be very good to have that fixed.

Apr 30, 2011 at 10:30 AM

There's already an "Upgraded Libs" branch in source control, unfortunately it doesn't work yet. But I assume that'll bring in the fixed NHibernate once it's integrated.

May 4, 2011 at 8:28 AM
Edited May 4, 2011 at 8:28 AM

Actually I've made fork for customizing database mappings and configuration.

I've made this to have orchard and my business data i one database and managed by the same NHibernate session.

http://orchard.codeplex.com/SourceControl/network/Forks/rodpl/lborchard

Jul 12, 2013 at 10:52 PM
Custom mapping overrides works now?
Jul 13, 2013 at 10:37 AM
It is officially supported to extend mappings and include in 1.7 version actually 1.x branch

Implement ISessionConfigurationEvents in your module to extend mappigns