我想使用Fluent NHibernate自动化为我的项目创建映射,但是我想通过另一种机制覆盖其中一些映射。该机制将基于模型类的自定义属性和一些反射登录来检查这些属性并执行必要的覆盖。
通过调用AutoPersistenceModel类中的Override<T>(Action<AutoMapping<T>> populateMap)
方法(FluentNHibernate.Automapping.AutoPersistenceModel)来进行覆盖。
我现在只尝试一种属性类型。我的想法是,如果我让它工作,我将创建更多的属性并改进它们。我目前拥有的一个属性称为MapIdAsAssignedAttribute,它应该执行与此相同的映射:
mapping.Id(x => x.Id).GeneratedBy.Assigned();
然而,我的方法并没有像我预期的那样有效。代码运行正常,并且当调用override方法时,我能够通过调试验证它在AutoPersistenceModel类中的inlineOverrides集合中进行的更改与在处理.UseOverridesFromAssemblyOf<T>()
时进行的更改相同找到一些实现IAutoMappingOverride<T>
的类。
问题是,当通过调用BuildSessionFactory()方法将配置内置到ISessionFactory对象中时,我通过调用Override方法以编程方式进行的覆盖不包含在配置中。配置就好像根本没有进行覆盖调用一样。
因此,使用IAutoMappingOverride<T>
类的覆盖有效,但调用Override<T>(Action<AutoMapping<T>> populateMap)
方法所做的覆盖不起作用。为了能够使用自定义属性的反射内容,我还需要使用Override方法。
我在这里做错了,或者这种行为是Fluent NHibernate中某些逻辑的结果?可能是一个错误?我相信这种程序化覆盖对许多其他项目也非常有用!
以下是我项目中相关课程的代码:
public class SessionFactoryCreator
{
private readonly string _connectionStringName;
public SessionFactoryCreator(string connectionStringName = null)
{
_connectionStringName = connectionStringName;
}
public virtual ISessionFactory CreateSessionFactory<T>(string connectionString = null)
{
connectionString = connectionString ?? GetConnectionString(_connectionStringName);
ISessionFactory sessionFactory = Configure<T>(connectionString).BuildSessionFactory();
return sessionFactory;
}
public virtual FluentConfiguration Configure<T>(string connectionString = null)
{
connectionString = connectionString ?? GetConnectionString(_connectionStringName);
AutoPersistenceModel autoMappings = CreateAutoMappings<T>();
FluentConfiguration configuration = Fluently.Configure()
.Database(GetPersistenceConfigurer(connectionString))
.Mappings(m => CreateFluentMappings(m))
.Mappings(m => m.AutoMappings.Add(autoMappings))
.ExposeConfiguration(c =>
{
c.SetProperty(Environment.TransactionStrategy, "NHibernate.Transaction.AdoNetTransactionFactory");
c.SetProperty("show_sql", "false");
});
return configuration;
}
protected virtual IPersistenceConfigurer GetPersistenceConfigurer(string connectionString)
{
IPersistenceConfigurer persistenceConfigurer = MsSqlConfiguration.MsSql2012.ConnectionString(connectionString);
return persistenceConfigurer;
}
protected virtual FluentMappingsContainer CreateFluentMappings(MappingConfiguration mappingConfiguration)
{
FluentMappingsContainer fluentMappingsContainer = mappingConfiguration.FluentMappings.AddFromAssemblyOf<SessionFactoryCreator>();
return fluentMappingsContainer;
}
protected virtual AutoPersistenceModel CreateAutoMappings<T>()
{
AutoPersistenceModel autoPersistenceModel = AutoMap.AssemblyOf<T>(new DefaultAutomappingConfiguration())
.Conventions.Add(new ForeignKeyNameConvention())
.Conventions.Add(new ReferenceNameConvention())
.Conventions.Add(new ForeignKeyConstraintNameConvention())
.Conventions.Add(new ManyToManyConvention())
.Conventions.Add(new CascadeAllConvention())
.UseOverridesFromAssemblyOf<T>();
foreach (Type type in Assembly.GetAssembly(typeof(T)).GetTypes())
{
if (type.GetCustomAttributes(typeof(MapIdAsAssignedAttribute), false).Length > 0)
{
MapIdAsAssignedOverrideForType(type, autoPersistenceModel);
}
}
return autoPersistenceModel;
}
private void MapIdAsAssignedOverrideForType(Type type, AutoPersistenceModel autoPersistenceModel)
{
MethodInfo method = GetType().GetMethod("MapIdAsAssigned", BindingFlags.Instance | BindingFlags.NonPublic);
MethodInfo genericMethodInfo = method.MakeGenericMethod(type);
genericMethodInfo.Invoke(this, new[] { autoPersistenceModel });
}
private void MapIdAsAssigned<T>(AutoPersistenceModel autoPersistenceModel)
{
autoPersistenceModel.Override<T>(map => map.Id().GeneratedBy.Assigned());
}
protected virtual string GetConnectionString(string connectionStringName)
{
if (string.IsNullOrEmpty(connectionStringName))
{
throw new ConfigurationErrorsException("Connection string name is not defined.");
}
ConnectionStringSettings connectionStringSettings = ConfigurationManager.ConnectionStrings[connectionStringName];
if (connectionStringSettings == null)
{
string message = string.Format("Could not find connection string with name '{0}'.", connectionStringName);
throw new ConfigurationErrorsException(message);
}
return connectionStringSettings.ConnectionString;
}