Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add mappings by namespace in Fluent NHibernate

In my application, I need to talk to multiple databases. I am handling this in NHibernate by creating one SessionFactory per database (I assume this is the correct thing to do). So I have two sets of models (one per database), and two sets of Fluent NHibernate ClassMap<> mappings. Both are in the same project (separated by namespace) and I'd like to keep it that way.

The problem comes when creating the SessionFactory. As far as I can see, Fluent NHibernate has basically two methods for adding mappings:

    .Mappings(m => m.FluentMappings.AddFromAssemblyOf<UserClassMap>())
    .Mappings(m => m.FluentMappings.Add<UserClassMap>()

If I use the first overload, then my session factories get all the mappings for both databases. If I use the second, I have to specify each individual ClassMap. I'd like something like FluentMappings.AddFromNamespace(). Is there a way to do this?

like image 422
Gabe Moothart Avatar asked Jun 01 '11 16:06

Gabe Moothart


2 Answers

It's odd that FluentNHibernate supports this type of filtering for automapping, but not for ClassMaps. It shouldn't be too hard to add this feature yourself, though, via the magic of extension methods. Try this:

public static FluentMappingsContainer AddFromAssemblyOf<T>(
    this FluentMappingsContainer mappings,
    Predicate<Type> where)
{
    if (where == null)
        return mappings.AddFromAssemblyOf<T>();

    var mappingClasses = typeof(T).Assembly.GetExportedTypes()
        .Where(x => (typeof(IMappingProvider).IsAssignableFrom(x)
                || typeof(IExternalComponentMappingProvider).IsAssignableFrom(x))
            && where(x));

    foreach (var type in mappingClasses)
    {
        mappings.Add(type);
    }

    return mappings;
}

... and use it like this:

m.FluentMappings.AddFromAssemblyOf<UserClassMap>(t => t.Namespace.StartsWith("One.Of.The.Two.Namespaces"));
like image 164
Daniel Schilling Avatar answered Sep 28 '22 16:09

Daniel Schilling


I wound up writing an extension method that does this for me. Basically it uses reflection to iterate over all the types I'm interested in, and add them one-by-one. It is based on the implementation of AddFromAssemblyOf. Usage:

.Mappings(m => m.FluentMappings.AddFromNamespaceOf<UserClassMap>())

Implementation:

public static class FluentNHibernateExtensions
{
    public static FluentMappingsContainer AddFromNamespaceOf<T>(
        this FluentMappingsContainer fmc)
    {
        string ns = typeof(T).Namespace;
        IEnumerable<Type> types = typeof(T).Assembly.GetExportedTypes()
            .Where(t => t.Namespace == ns)
            .Where(x => IsMappingOf<IMappingProvider>(x) ||
                        IsMappingOf<IIndeterminateSubclassMappingProvider>(x) ||
                        IsMappingOf<IExternalComponentMappingProvider>(x) ||
                        IsMappingOf<IFilterDefinition>(x));

        foreach(Type t in types) {
            fmc.Add(t);
        }

        return fmc;
    }

    /// <summary>
    /// Private helper method cribbed from FNH source (PersistenModel.cs:151)
    /// </summary>
    private static bool IsMappingOf<T>(Type type)
    {
        return !type.IsGenericType && typeof(T).IsAssignableFrom(type);
    }
}

Caveats:

  • The name is a little misleading, since it only searches one assembly. It should perhaps be called AddFromAssemblyAndNamespaceOf, but that's a little verbose.
  • It is not entirely future-proof. If future versions of FNH add or remove some of the mappable interfaces, it wouldn't include them.

But it works for my purposes.

like image 29
Gabe Moothart Avatar answered Sep 28 '22 16:09

Gabe Moothart