The auto-mapping's customizer adapts PostgreSQL lowercase+underscore table and property naming convention to .NET's Pascal-case class and property naming convention
using NHibernate.Cfg; // .DatabaseIntegration extension method
namespace Erp.DomainMapping
{
public static class Mapper
{
static NHibernate.ISessionFactory _sessionFactory = Mapper.BuildSessionFactory();
// call this on production
public static NHibernate.ISessionFactory SessionFactory
{
get { return _sessionFactory; }
}
public static NHibernate.ISessionFactory BuildSessionFactory(bool useUnitTest = false)
{
var mapper = new NHibernate.Mapping.ByCode.ConventionModelMapper();
mapper.IsEntity((t, declared) => t.Namespace == "Erp.Domain.TheModels");
mapper.BeforeMapClass += mapper_BeforeMapClass;
mapper.BeforeMapProperty += mapper_BeforeMapProperty;
mapper.BeforeMapManyToOne += mapper_BeforeMapManyToOne;
mapper.BeforeMapBag += mapper_BeforeMapBag;
var cfg = new NHibernate.Cfg.Configuration();
// .DatabaseIntegration! Y U EXTENSION METHOD?!
cfg.DataBaseIntegration(c =>
{
var cs = System.Configuration.ConfigurationManager.ConnectionStrings["TheErpConnection"].ConnectionString;
//// SQL Server
//c.Driver<NHibernate.Driver.SqlClientDriver>();
//c.Dialect<NHibernate.Dialect.MsSql2008Dialect>();
//c.ConnectionString = "Server=.;Database=TestTheDatabase;Trusted_Connection=True";
// PostgreSQL
c.Driver<NHibernate.Driver.NpgsqlDriver>();
c.Dialect<NHibernate.Dialect.PostgreSQLDialect>();
c.ConnectionString = cs;
if (useUnitTest)
{
c.LogSqlInConsole = true;
c.LogFormattedSql = true;
}
});
NHibernate.Cfg.MappingSchema.HbmMapping mapping =
mapper.CompileMappingFor(typeof(Erp.Domain.TheModels.Company).Assembly.GetExportedTypes());
cfg.AddMapping(mapping);
// http://www.ienablemuch.com/2013/06/multilingual-and-caching-on-nhibernate.html
//var filterDef = new NHibernate.Engine.FilterDefinition("lf", /*default condition*/ null,
// new Dictionary<string, NHibernate.Type.IType>
// {
// { "LanguageCultureCode", NHibernate.NHibernateUtil.String}
// }, useManyToOne: false);
//cfg.AddFilterDefinition(filterDef);
cfg.Cache(x =>
{
// SysCache is not stable on unit testing
if (!useUnitTest)
{
x.Provider<NHibernate.Caches.SysCache.SysCacheProvider>();
// I don't know why SysCacheProvider is not stable on simultaneous unit testing,
// might be SysCacheProvider is just giving one session factory, so simultaneous test see each other caches
// This solution doesn't work: http://stackoverflow.com/questions/700043/mstest-executing-all-my-tests-simultaneously-breaks-tests-what-to-do
}
else
{
// This is more stable in unit testing
x.Provider<NHibernate.Cache.HashtableCacheProvider>();
}
// http://stackoverflow.com/questions/2365234/how-does-query-caching-improves-performance-in-nhibernate
// Need to be explicitly turned on so the .Cacheable directive on Linq will work:
x.UseQueryCache = true;
});
if (useUnitTest)
cfg.SetInterceptor(new NHSQLInterceptor());
//new NHibernate.Tool.hbm2ddl.SchemaUpdate(cfg).Execute(useStdOut: false, doUpdate: true);
//using (var file = new System.IO.FileStream(@"c:\x\ddl.txt",
// System.IO.FileMode.Create,
// System.IO.FileAccess.ReadWrite))
//using (var sw = new System.IO.StreamWriter(file))
//{
// new SchemaUpdate(cfg)
// .Execute(sw.Write, false);
//}
var sf = cfg.BuildSessionFactory();
return sf;
}
static void mapper_BeforeMapProperty(NHibernate.Mapping.ByCode.IModelInspector modelInspector,
NHibernate.Mapping.ByCode.PropertyPath member,
NHibernate.Mapping.ByCode.IPropertyMapper propertyCustomizer)
{
string postgresFriendlyName = member.ToColumnName().ToLowercaseNamingConvention();
propertyCustomizer.Column(postgresFriendlyName);
}
static void mapper_BeforeMapClass(NHibernate.Mapping.ByCode.IModelInspector modelInspector,
System.Type type,
NHibernate.Mapping.ByCode.IClassAttributesMapper classCustomizer)
{
classCustomizer.Cache(cacheMapping => cacheMapping.Usage(NHibernate.Mapping.ByCode.CacheUsage.ReadWrite));
string className = type.Name;
string postgresFriendlyName = className.ToLowercaseNamingConvention();
classCustomizer.Table(postgresFriendlyName);
System.Reflection.MemberInfo mi = type.GetMember(className + "Id",
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)[0];
classCustomizer.Id(mi,
idMapper =>
{
idMapper.Column(postgresFriendlyName + "_id");
idMapper.Generator(
NHibernate.Mapping.ByCode.Generators.Sequence,
generatorMapping => generatorMapping.Params(new { sequence = postgresFriendlyName + "_" + postgresFriendlyName + "_id_seq" }));
});
}
static void mapper_BeforeMapManyToOne(
NHibernate.Mapping.ByCode.IModelInspector modelInspector,
NHibernate.Mapping.ByCode.PropertyPath member,
NHibernate.Mapping.ByCode.IManyToOneMapper propertyCustomizer)
{
string postgresFriendlyName = member.ToColumnName().ToLowercaseNamingConvention() + "_id";
propertyCustomizer.Column(postgresFriendlyName);
}
static void mapper_BeforeMapBag(
NHibernate.Mapping.ByCode.IModelInspector modelInspector,
NHibernate.Mapping.ByCode.PropertyPath member,
NHibernate.Mapping.ByCode.IBagPropertiesMapper propertyCustomizer)
{
propertyCustomizer.Cache(cacheMapping => cacheMapping.Usage(NHibernate.Mapping.ByCode.CacheUsage.ReadWrite));
propertyCustomizer.Lazy(NHibernate.Mapping.ByCode.CollectionLazy.Extra);
/*
* class Person
* {
* IList<Hobby> Hobbies
* }
*
*/
string parentEntity = member.LocalMember.DeclaringType.Name.ToLowercaseNamingConvention(); // this gets the Person
string foreignKey = parentEntity + "_id";
propertyCustomizer.Key(keyMapping => keyMapping.Column(foreignKey));
// http://www.ienablemuch.com/2014/10/inverse-cascade-variations-on-nhibernate.html
// best persistence approach: Inverse+CascadeAll
propertyCustomizer.Inverse(true);
propertyCustomizer.Cascade(NHibernate.Mapping.ByCode.Cascade.All);
}
class NHSQLInterceptor : NHibernate.EmptyInterceptor
{
// http://stackoverflow.com/questions/2134565/how-to-configure-fluent-nhibernate-to-output-queries-to-trace-or-debug-instead-o
public override NHibernate.SqlCommand.SqlString OnPrepareStatement(NHibernate.SqlCommand.SqlString sql)
{
Mapper.NHibernateSQL = sql.ToString();
return sql;
}
}
public static string NHibernateSQL { get; set; }
} // Mapper
static class StringHelper
{
public static string ToLowercaseNamingConvention(this string s, bool toLowercase = true)
{
if (toLowercase)
{
var r = new System.Text.RegularExpressions.Regex(@"
(?<=[A-Z])(?=[A-Z][a-z]) |
(?<=[^A-Z])(?=[A-Z]) |
(?<=[A-Za-z])(?=[^A-Za-z])", System.Text.RegularExpressions.RegexOptions.IgnorePatternWhitespace);
return r.Replace(s, "_").ToLower();
}
else
return s;
}
}
}
Happy Coding!
No comments:
Post a Comment