Here I’ll try to explain how we have used it in that application writing a new example.
The IModuleMapper
/// 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.ModuleAusing 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;
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 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.
{
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 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 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:{
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();