How to add mappings by namespace in Fluent NHibernate
Asked Answered
L

4

9

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?

Lawful answered 1/6, 2011 at 16:25 Comment(0)
Z
17

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"));
Zoospore answered 1/6, 2011 at 20:17 Comment(2)
+1 I discovered something similar, but this approach is more general.Lawful
This doesn't map SubClassMaps, as they aren't assignable from IMappingProvider. https://mcmap.net/q/1125097/-how-to-add-mappings-by-namespace-in-fluent-nhibernate has an expanded list of MappingProviders that should be checked.Celiotomy
L
10

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.

Lawful answered 1/6, 2011 at 20:19 Comment(1)
This is exactly what I needed - thank you! This should just be built into FNH.Mauney
L
0

There is no way to do this. I recommend separating the namespaces out into separate projects. Remember:

Separate namespaces, same project when logical separation makes sense. Separate namespaces, separate projects when physical separation makes sense.

In this case, since you can't separate by namespace in nhibernate mappings, physical separation makes sense. You can, however, get around this with with fluent automaps that use a .Where or a ShouldMap configuration. Look up fluent automaps and see if that can get you where you want to be.

Loop answered 1/6, 2011 at 19:47 Comment(0)
H
0
... AutoMap.AssemblyOf<Person>().Where(x => x.Namespace.EndsWith("Domain")) ...
Heribertoheringer answered 1/6, 2011 at 19:51 Comment(1)
I'm not using automappings, I'm specifying my mappings explicitly with ClassMap<> mappings.Lawful

© 2022 - 2024 — McMap. All rights reserved.