在fluentnhibernate automapper中为接口提供AutoMappingOverride的最好方法是什么? [英] What is the best way to provide an AutoMappingOverride for an interface in fluentnhibernate automapper
问题描述
在系统中寻找应用程序的版本范围的数据库过滤器时,我写了下面的代码: ;
using System.Collections.Generic;
使用System.Linq;
使用System.Text;
使用FluentNHibernate.Automapping;
使用FluentNHibernate.Automapping.Alterations;
使用FluentNHibernate.Mapping;
使用MvcExtensions.Model;
使用NHibernate;
namespace MvcExtensions.Services.Impl.FluentNHibernate
{
public interface IVersionAware
{
string Version {get;组; }
$ b $ public class VersionFilter:FilterDefinition
{
const string FILTERNAME =MyVersionFilter;
const string COLUMNNAME =Version;
public VersionFilter()
{
this.WithName(FILTERNAME)
.WithCondition(Version =:+ COLUMNNAME)
.AddParameter(COLUMNNAME ,NHibernateUtil.String);
$ b $ public static void EnableVersionFilter(ISession session,string version)
{
session.EnableFilter(FILTERNAME).SetParameter(COLUMNNAME,version);
}
public static void DisableVersionFilter(ISession session)
{
session.DisableFilter(FILTERNAME);
}
}
public class VersionAwareOverride:IAutoMappingOverride< IVersionAware>
{
#region IAutoMappingOverride< IVersionAware>成员
public void Override(AutoMapping< IVersionAware>映射)
{
mapping.ApplyFilter< VersionFilter>();
}
#endregion
}
}
但是,由于覆盖不能在接口上工作,我正在寻找一种方法来实现这一点。
目前我为每个实现接口的类使用了这个(相当麻烦的)方法:
$ b $ $ $ $ $ $ $ $ $ $ public class SomeVersionedEntity :IModelId,IVersionAware
{
public virtual int Id {get;组; }
public virtual string Version {get;组; }
}
public class SomeVersionedEntityOverride:IAutoMappingOverride< SomeVersionedEntity>
{
#region IAutoMappingOverride< SomeVersionedEntity>成员
public void Override(AutoMapping< SomeVersionedEntity>映射)
{
mapping.ApplyFilter< VersionFilter>();
}
#endregion
}
I一直在看IClassmap接口等,但他们似乎没有提供一种方法来访问ApplyFilter方法,所以我没有得到一个线索在这里...
从我可能不是第一个有这个问题的人,我相信这是可能的。我只是不太确定这是如何工作的。
编辑:
我已经接近一个通用的解决方案:
这是我试图解决这个问题的方法:
使用泛型类来实现对实现类的更改一个接口:
public abstract class AutomappingInterfaceAlteration< I> :IAutoMappingAlteration
{
public void Alter(AutoPersistenceModel model)
{
model.OverrideAll(map =>
{
var recordType = map.GetType ().GetGenericArguments()。Single();
if(typeof(I).IsAssignableFrom(recordType))
{
this.GetType()。GetMethod(overrideStuff)。MakeGenericMethod (recordType).Invoke(this,new object [] {model});
}
});
public void overrideStuff< T>(AutoPersistenceModel pm)其中T:I
{
pm.Override< T>(a => Override(a ));
public abstract void Override< T>(AutoMapping< T> am)其中T:I;
}
一个具体的实现:
public class VersionAwareAlteration:AutomappingInterfaceAteration< IVersionAware>
{
public override void Override< T>(AutoMapping< T> am)
{
am.Map(x => x.Version).Column(VersionTest );
am.ApplyFilter< VersionFilter>();
$ b不幸的是现在我得到了下面的错误: [InvalidOperationException:集合被修改;枚举操作可能不会执行。]
System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource资源)+51
System.Collections.Generic.Enumerator.MoveNextRare()+7661017
System.Collections.Generic.Enumerator .MoveNext()+61
System.Linq.WhereListIterator`1.MoveNext()+156
FluentNHibernate.Utils.CollectionExtensions.Each(IEnumerable`1 enumerable,Action`1 each)+239 $ b $ (类型classType,IList`1 mappedProperties,ClassMappingBase映射)+345
FluentNHibernate.Automapping.AutoMapper.MergeMap(类型classType,ClassMappingBase映射,IList`1 mappedProperties)+43 $ b $ FluentNHibernate.Automapping.AutoMapper.ApplyOverrides b FluentNHibernate.Automapping.AutoMapper.Map(类型classType,List`1类型)+566
FluentNHibernate.Automapping.AutoPersistenceModel.AddMapping(类型类型)+85
FluentNHibernate.Automapping.AutoPersistenceModel.CompileMappings()+ 746
编辑2:我设法进一步得到;我现在使用反射为每个实现接口的类调用覆盖:
public抽象类PersistenceOverride< I>
{
$ b $ public void DoOverrides(AutoPersistenceModel model,IEnumerable< Type> Mytypes)
{
foreach(var in Mytypes.Where(x => typeof I).IsAssignableFrom(x)))
ManualOverride(t,model);
$ b $ private void ManualOverride(Type recordType,AutoPersistenceModel model)
{
var t_amt = typeof(AutoMapping<>)。
var t_act = typeof(Action<)。MakeGenericType(t_amt);
var m = typeof(PersistenceOverride< I>)
.GetMethod(MyOverride)
.MakeGenericMethod(recordType)
.Invoke(this,null);
model.GetType()。GetMethod(Override)。MakeGenericMethod(recordType).Invoke(model,new object [] {m});
}
public abstract Action< AutoMapping< T>> MyOverride< T>()其中T:I;
}
public class VersionAwareOverride:PersistenceOverride< IVersionAware>
{
public override Action< AutoMapping< T>> MyOverride< T>()
{
return am =>
{
am.Map(x => x.Version).Column(VersionFilter.COLUMNNAME);
am.ApplyFilter< VersionFilter>();
};
$ b但是,由于某种原因,我生成的hbm文件不要包含任何过滤器字段....
也许有人可以帮助我更进一步呢??
解决方案 <显然,当前版本的fluent-nhibernate有一个bug。当您使用Automapper.Override< T>时,过滤器不会被复制。 。
我分叉源代码,修正了错误,测试了一下,现在我已经发送了一个pull请求给github上的fluentnhib工具。
现在,您可以在 http: //github.com/ToJans/fluent-nhibernate/commit/29058de9b2bc3af85bc433aa6f71549f7b5d8e04
现在还有一个关于如何做到的完整博客文章:
http://www.corebvba.be/blog/post/How-to-override-interface-mappings-and-creata-a-generic-entity-version-filter-in- fluent-nhibernate.aspx
In my quest for a version-wide database filter for an application, I have written the following code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentNHibernate.Automapping;
using FluentNHibernate.Automapping.Alterations;
using FluentNHibernate.Mapping;
using MvcExtensions.Model;
using NHibernate;
namespace MvcExtensions.Services.Impl.FluentNHibernate
{
public interface IVersionAware
{
string Version { get; set; }
}
public class VersionFilter : FilterDefinition
{
const string FILTERNAME = "MyVersionFilter";
const string COLUMNNAME = "Version";
public VersionFilter()
{
this.WithName(FILTERNAME)
.WithCondition("Version = :"+COLUMNNAME)
.AddParameter(COLUMNNAME, NHibernateUtil.String );
}
public static void EnableVersionFilter(ISession session,string version)
{
session.EnableFilter(FILTERNAME).SetParameter(COLUMNNAME, version);
}
public static void DisableVersionFilter(ISession session)
{
session.DisableFilter(FILTERNAME);
}
}
public class VersionAwareOverride : IAutoMappingOverride<IVersionAware>
{
#region IAutoMappingOverride<IVersionAware> Members
public void Override(AutoMapping<IVersionAware> mapping)
{
mapping.ApplyFilter<VersionFilter>();
}
#endregion
}
}
But, since overrides do not work on interfaces, I am looking for a way to implement this.
Currently I'm using this (rather cumbersome) way for each class that implements the interface :
public class SomeVersionedEntity : IModelId, IVersionAware
{
public virtual int Id { get; set; }
public virtual string Version { get; set; }
}
public class SomeVersionedEntityOverride : IAutoMappingOverride<SomeVersionedEntity>
{
#region IAutoMappingOverride<SomeVersionedEntity> Members
public void Override(AutoMapping<SomeVersionedEntity> mapping)
{
mapping.ApplyFilter<VersionFilter>();
}
#endregion
}
I have been looking at IClassmap interfaces etc, but they do not seem to provide a way to access the ApplyFilter method, so I have not got a clue here...
Since I am probably not the first one who has this problem, I am quite sure that it should be possible; I am just not quite sure how this works..
EDIT :
I have gotten a bit closer to a generic solution:
This is the way I tried to solve it :
Using a generic class to implement alterations to classes implementing an interface :
public abstract class AutomappingInterfaceAlteration<I> : IAutoMappingAlteration
{
public void Alter(AutoPersistenceModel model)
{
model.OverrideAll(map =>
{
var recordType = map.GetType().GetGenericArguments().Single();
if (typeof(I).IsAssignableFrom(recordType))
{
this.GetType().GetMethod("overrideStuff").MakeGenericMethod(recordType).Invoke(this, new object[] { model });
}
});
}
public void overrideStuff<T>(AutoPersistenceModel pm) where T : I
{
pm.Override<T>( a => Override(a));
}
public abstract void Override<T>(AutoMapping<T> am) where T:I;
}
And a specific implementation :
public class VersionAwareAlteration : AutomappingInterfaceAlteration<IVersionAware>
{
public override void Override<T>(AutoMapping<T> am)
{
am.Map(x => x.Version).Column("VersionTest");
am.ApplyFilter<VersionFilter>();
}
}
Unfortunately I get the following error now :
[InvalidOperationException: Collection was modified; enumeration operation may not execute.]
System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource) +51
System.Collections.Generic.Enumerator.MoveNextRare() +7661017
System.Collections.Generic.Enumerator.MoveNext() +61
System.Linq.WhereListIterator`1.MoveNext() +156
FluentNHibernate.Utils.CollectionExtensions.Each(IEnumerable`1 enumerable, Action`1 each) +239
FluentNHibernate.Automapping.AutoMapper.ApplyOverrides(Type classType, IList`1 mappedProperties, ClassMappingBase mapping) +345
FluentNHibernate.Automapping.AutoMapper.MergeMap(Type classType, ClassMappingBase mapping, IList`1 mappedProperties) +43
FluentNHibernate.Automapping.AutoMapper.Map(Type classType, List`1 types) +566
FluentNHibernate.Automapping.AutoPersistenceModel.AddMapping(Type type) +85
FluentNHibernate.Automapping.AutoPersistenceModel.CompileMappings() +746
EDIT 2 : I managed to get a bit further; I now invoke "Override" using reflection for each class that implements the interface :
public abstract class PersistenceOverride<I>
{
public void DoOverrides(AutoPersistenceModel model,IEnumerable<Type> Mytypes)
{
foreach(var t in Mytypes.Where(x=>typeof(I).IsAssignableFrom(x)))
ManualOverride(t,model);
}
private void ManualOverride(Type recordType,AutoPersistenceModel model)
{
var t_amt = typeof(AutoMapping<>).MakeGenericType(recordType);
var t_act = typeof(Action<>).MakeGenericType(t_amt);
var m = typeof(PersistenceOverride<I>)
.GetMethod("MyOverride")
.MakeGenericMethod(recordType)
.Invoke(this, null);
model.GetType().GetMethod("Override").MakeGenericMethod(recordType).Invoke(model, new object[] { m });
}
public abstract Action<AutoMapping<T>> MyOverride<T>() where T:I;
}
public class VersionAwareOverride : PersistenceOverride<IVersionAware>
{
public override Action<AutoMapping<T>> MyOverride<T>()
{
return am =>
{
am.Map(x => x.Version).Column(VersionFilter.COLUMNNAME);
am.ApplyFilter<VersionFilter>();
};
}
}
However, for one reason or another my generated hbm files do not contain any "filter" fields....
Maybe somebody could help me a bit further now ??
解决方案 Apparently, there was a bug in the current version of fluent-nhibernate. The filters did not get copied when you use Automapper.Override<T> .
I forked the source, fixed the bug, tested, and I have now sent a pull request to the fluentnhib guys over at github.
For now, you can download the fixed sourcecode at http://github.com/ToJans/fluent-nhibernate/commit/29058de9b2bc3af85bc433aa6f71549f7b5d8e04
There is now also a complete blog post on how to do this :
http://www.corebvba.be/blog/post/How-to-override-interface-mappings-and-creata-a-generic-entity-version-filter-in-fluent-nhibernate.aspx
这篇关于在fluentnhibernate automapper中为接口提供AutoMappingOverride的最好方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!