Here I’ll try to explain how we have used it in that application writing a new example.
The IModuleMapper
///
<summary>
/// A template to perform a mapping using ConfOrm
/// </summary>
public interface IModuleMapper
{
/// <summary>
/// Register domain classes, persistent-strategies, relations and so on of a spefic module
/// </summary>
void DefineDomain();
/// <summary>
/// Register patterns of a module
/// </summary>
void RegisterPatterns();
/// <summary>
/// Customize persistence representations
/// </summary>
void Customize();
/// <summary>
/// Get all domain entities of the module.
/// </summary>
/// <returns> domain entities. </returns>
IEnumerable< Type> GetEntities();
}
/// A template to perform a mapping using ConfOrm
/// </summary>
public interface IModuleMapper
{
/// <summary>
/// Register domain classes, persistent-strategies, relations and so on of a spefic module
/// </summary>
void DefineDomain();
/// <summary>
/// Register patterns of a module
/// </summary>
void RegisterPatterns();
/// <summary>
/// Customize persistence representations
/// </summary>
void Customize();
/// <summary>
/// Get all domain entities of the module.
/// </summary>
/// <returns> domain entities. </returns>
IEnumerable< Type> GetEntities();
}
The Domain
In the assembly Acme.ModuleA
using System;
using Acme.Domain.Common;
namespace Acme.ModuleA
{
public class User: Entity
{
public string UserName { get; set; }
public string Text { get; set; }
public DateTime Birthday { get; set; }
}
}
using Acme.Domain.Common;
namespace Acme.ModuleA
{
public class User: Entity
{
public string UserName { get; set; }
public string Text { get; set; }
public DateTime Birthday { get; set; }
}
}
using System;
using Acme.Domain.Common;
using Acme.ModuleA;
namespace Acme.ModuleB
{
public class Article: Entity
{
public DateTime PublishedAt { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public User Author { get; set; }
}
}
using Acme.Domain.Common;
using Acme.ModuleA;
namespace Acme.ModuleB
{
public class Article: Entity
{
public DateTime PublishedAt { get; set; }
public string Title { get; set; }
public string Text { get; set; }
public User Author { get; set; }
}
}
Some extensions
Just to not forget it later…
public
static
class
ModuleMappingUtil
{
public static IEnumerable< Type> MapModule( this IModuleMapper mapper)
{
mapper.DefineDomain();
mapper.RegisterPatterns();
mapper.Customize();
return mapper.GetEntities();
}
public static bool IsOfModuleContaining<T>( this Type source)
{
return source.Assembly.Equals( typeof(T).Assembly);
}
}
{
public static IEnumerable< Type> MapModule( this IModuleMapper mapper)
{
mapper.DefineDomain();
mapper.RegisterPatterns();
mapper.Customize();
return mapper.GetEntities();
}
public static bool IsOfModuleContaining<T>( this Type source)
{
return source.Assembly.Equals( typeof(T).Assembly);
}
}
The mapping
You can organize the mapping of your modules in a single assembly, in general the same where you are generating the session factory, or in more than one assembly. In general you need just a little class to map a module so please don’t be exaggerated separating your application.For this example I will show three implementations of ModuleMapper all implemented in the same assembly named Acme.Persistence.Wiring.
public
class
GeneralsPatternsModuleMapper :
IModuleMapper
{
private readonly Mapper mapper;
private readonly ObjectRelationalMapper orm;
public GeneralsPatternsModuleMapper( ObjectRelationalMapper orm, Mapper mapper)
{
this.orm = orm;
this.mapper = mapper;
}
#region IModuleMapper Members
public void DefineDomain()
{
// map .NET4 ISet<T> as a NHibernate's set
orm.Patterns.Sets.Add(mi => mi.GetPropertyOrFieldType().GetGenericIntercafesTypeDefinitions().Contains( typeof ( ISet<>)));
}
public void RegisterPatterns()
{
// all strings are length 50 characters
mapper.PatternsAppliers.Property.Add(mi => typeof ( string).Equals(mi.GetPropertyOrFieldType()), (mi, map) => map.Length(50));
// when a DateTime seems to be a simple date apply NH's Date type
mapper.PatternsAppliers.Property.Add(mi => typeof ( DateTime).Equals(mi.GetPropertyOrFieldType()) && IsDate(mi.Name), (mi, map) => map.Type( NHibernateUtil.Date));
// when a DateTime seems to not be a simple date apply NH's UtcDateTime type
mapper.PatternsAppliers.Property.Add(mi => typeof ( DateTime).Equals(mi.GetPropertyOrFieldType()) && !IsDate(mi.Name), (mi, map) => map.Type( NHibernateUtil.UtcDateTime));
}
public void Customize()
{
// Nothing to do
}
public IEnumerable< Type> GetEntities()
{
yield break;
}
#endregion
public static bool IsDate( string name)
{
return name.EndsWith( "Date") || name.StartsWith( "Date") || name.EndsWith( "Day") || name.EndsWith( "day") || name.StartsWith( "Day");
}
}
{
private readonly Mapper mapper;
private readonly ObjectRelationalMapper orm;
public GeneralsPatternsModuleMapper( ObjectRelationalMapper orm, Mapper mapper)
{
this.orm = orm;
this.mapper = mapper;
}
#region IModuleMapper Members
public void DefineDomain()
{
// map .NET4 ISet<T> as a NHibernate's set
orm.Patterns.Sets.Add(mi => mi.GetPropertyOrFieldType().GetGenericIntercafesTypeDefinitions().Contains( typeof ( ISet<>)));
}
public void RegisterPatterns()
{
// all strings are length 50 characters
mapper.PatternsAppliers.Property.Add(mi => typeof ( string).Equals(mi.GetPropertyOrFieldType()), (mi, map) => map.Length(50));
// when a DateTime seems to be a simple date apply NH's Date type
mapper.PatternsAppliers.Property.Add(mi => typeof ( DateTime).Equals(mi.GetPropertyOrFieldType()) && IsDate(mi.Name), (mi, map) => map.Type( NHibernateUtil.Date));
// when a DateTime seems to not be a simple date apply NH's UtcDateTime type
mapper.PatternsAppliers.Property.Add(mi => typeof ( DateTime).Equals(mi.GetPropertyOrFieldType()) && !IsDate(mi.Name), (mi, map) => map.Type( NHibernateUtil.UtcDateTime));
}
public void Customize()
{
// Nothing to do
}
public IEnumerable< Type> GetEntities()
{
yield break;
}
#endregion
public static bool IsDate( string name)
{
return name.EndsWith( "Date") || name.StartsWith( "Date") || name.EndsWith( "Day") || name.EndsWith( "day") || name.StartsWith( "Day");
}
}
public
class
ModuleAMapper :
IModuleMapper
{
private readonly Mapper mapper;
private readonly ObjectRelationalMapper orm;
public ModuleAMapper( ObjectRelationalMapper orm, Mapper mapper)
{
this.orm = orm;
this.mapper = mapper;
}
public void DefineDomain()
{
// register all classes of my module (ConfORM will use it to discover polymorphic associations)
orm.AddToDomain( typeof( User).Assembly.GetExportedTypes());
// defines the persistence strategy for each root of each hierarchy
orm.TablePerClass< User>();
}
public void RegisterPatterns()
{
// no specific patterns for this module
}
public void Customize()
{
// map a specific size (20) for a specific property of a specific class
mapper.Class< User>(x => x.Property(user => user.UserName, map => map.Length(20)));
}
public IEnumerable< Type> GetEntities()
{
// all entities of this modules
return typeof( User).Assembly.GetExportedTypes().Where(t=> typeof( Entity).IsAssignableFrom(t));
}
}
{
private readonly Mapper mapper;
private readonly ObjectRelationalMapper orm;
public ModuleAMapper( ObjectRelationalMapper orm, Mapper mapper)
{
this.orm = orm;
this.mapper = mapper;
}
public void DefineDomain()
{
// register all classes of my module (ConfORM will use it to discover polymorphic associations)
orm.AddToDomain( typeof( User).Assembly.GetExportedTypes());
// defines the persistence strategy for each root of each hierarchy
orm.TablePerClass< User>();
}
public void RegisterPatterns()
{
// no specific patterns for this module
}
public void Customize()
{
// map a specific size (20) for a specific property of a specific class
mapper.Class< User>(x => x.Property(user => user.UserName, map => map.Length(20)));
}
public IEnumerable< Type> GetEntities()
{
// all entities of this modules
return typeof( User).Assembly.GetExportedTypes().Where(t=> typeof( Entity).IsAssignableFrom(t));
}
}
public
class
ModuleBMapper :
IModuleMapper
{
private readonly Mapper mapper;
private readonly ObjectRelationalMapper orm;
public ModuleBMapper( ObjectRelationalMapper orm, Mapper mapper)
{
this.orm = orm;
this.mapper = mapper;
}
public void DefineDomain()
{
// register all classes of my module (ConfORM will use it to discover polymorphic associations)
orm.AddToDomain( typeof( Article).Assembly.GetExportedTypes());
// defines the persistence strategy for each root of each hierarchy
orm.TablePerClassHierarchy< Article>();
}
public void RegisterPatterns()
{
// patterns for this module
// when a string property is named "Text" then apply StringClob type (note just for Acme.ModuleB)
mapper.PatternsAppliers.Property.Add(
mi => "text".Equals(mi.Name.ToLowerInvariant()) && typeof ( string).Equals(mi.GetPropertyOrFieldType()) && mi.DeclaringType.IsOfModuleContaining< Article>(),
(mi, map) => { map.Type( NHibernateUtil.StringClob); map.Length( int.MaxValue); });
}
public void Customize()
{
// nothing to do
}
public IEnumerable< Type> GetEntities()
{
// all entities of this modules
return typeof( Article).Assembly.GetExportedTypes().Where(t => typeof( Entity).IsAssignableFrom(t));
}
}
{
private readonly Mapper mapper;
private readonly ObjectRelationalMapper orm;
public ModuleBMapper( ObjectRelationalMapper orm, Mapper mapper)
{
this.orm = orm;
this.mapper = mapper;
}
public void DefineDomain()
{
// register all classes of my module (ConfORM will use it to discover polymorphic associations)
orm.AddToDomain( typeof( Article).Assembly.GetExportedTypes());
// defines the persistence strategy for each root of each hierarchy
orm.TablePerClassHierarchy< Article>();
}
public void RegisterPatterns()
{
// patterns for this module
// when a string property is named "Text" then apply StringClob type (note just for Acme.ModuleB)
mapper.PatternsAppliers.Property.Add(
mi => "text".Equals(mi.Name.ToLowerInvariant()) && typeof ( string).Equals(mi.GetPropertyOrFieldType()) && mi.DeclaringType.IsOfModuleContaining< Article>(),
(mi, map) => { map.Type( NHibernateUtil.StringClob); map.Length( int.MaxValue); });
}
public void Customize()
{
// nothing to do
}
public IEnumerable< Type> GetEntities()
{
// all entities of this modules
return typeof( Article).Assembly.GetExportedTypes().Where(t => typeof( Entity).IsAssignableFrom(t));
}
}
The NHibernate’s initialization
First you need a method to get all modules of your application, for simplicity here is a simple implementation:
private
static
IEnumerable<
IModuleMapper> GetAllModulesMappers(
ObjectRelationalMapper orm,
Mapper mapper)
{
yield return new GeneralsPatternsModuleMapper(orm, mapper);
yield return new ModuleAMapper(orm, mapper);
yield return new ModuleBMapper(orm, mapper);
}
{
yield return new GeneralsPatternsModuleMapper(orm, mapper);
yield return new ModuleAMapper(orm, mapper);
yield return new ModuleBMapper(orm, mapper);
}
- var orm = new ObjectRelationalMapper();
- var patternSet = new CoolPatternsAppliersHolder(orm);
- var mapper = new Mapper(orm, patternSet);
- HbmMapping mappings = mapper.CompileMappingFor(GetAllModulesMappers(orm, mapper).SelectMany(x => x.MapModule()));
- var configure = new Configuration();
- configure.SessionFactoryName("Demo");
- configure.DataBaseIntegration(db =>
- {
- db.Dialect<MsSql2008Dialect>();
- db.ConnectionStringName = "ToAcmeDb";
- });
- configure.AddDeserializedMapping(mappings, "AcmeApplicationDomain");
- ISessionFactory factory = configure.BuildSessionFactory();