我正在解决一个问题,其中我有多个实体,每个实体都有一个相应的一对多翻译表,该表指定实体字段的本地化版本。 (所有这些都是遗留模式,我在其上添加了地图)。例如:
因此,如果我要以希腊语呈现我的信息,我将连接两个表并指定 Language = 'Greek' 并拥有所有正确的内容。
我想做的是构建即时混合,直接将正确的数据合并到单个对象中,并将其作为查询结果返回,就像:
var someEvent = session.CreateCriteria<Event>().SetMaxResults(1).UniqueResult<IEvent>();
Console.WriteLine(someEvent.Title);
为此,我正在尝试设置一个 NHibernate拦截器来创建 DynamicProxy mixins。但它不起作用,我不知道为什么。这是设置,我已尽我所能简化它。
这是事件:
class Event : IEventEntity {
//ID and props here
public IList Translations {get; set;}
}
IEvenEntity
也有列表的 getter 和 setter。还有一个 EventTranslation
类,它非常简单,并以最明显的方式实现 IEventTranslation
。
事件的流畅地图:
class EventMap : ClassMap<Event>{
//obvious ID and properties stuff here...
HasMany<EventTranslation>(x => x.Translations);
}
它本身工作得很好——我可以查询事件并导航到它们的翻译。我很确定映射是好的。
我的拦截器的形状基于一个非常酷的 Krzysztof Koźmic 的指南,做了一些稍微相关的事情。首先,我创建了一个超级接口,我将使用动态代理来实现它:
public interface IEvent : IEventEntity, IEventTranslation{}
这是我的 NH 拦截器。显然,我正在疯狂地进行黑客攻击:
public class EventInterceptor : NHibernate.EmptyInterceptor
{
private readonly static ProxyGenerator gen = new ProxyGenerator();
public override object Instantiate(string clazz, NHibernate.EntityMode entityMode, object id)
{
var mixin = gen.CreateClassProxy(typeof(object), new[] { typeof(IEvent) }, new DynInterceptor());
//would also need to set the identifier here
return mixin;
}
}
其中 DynInterceptor 是一个拦截器,它实际上执行进入关系包、获得正确翻译并返回正确值的工作。细节不太相关,因为它永远不会被调用。
在绑定 NH 拦截器后,我看到它正在运行,事实上 NH 正确地转换为 IEvent
(即至少正在创建代理)。但由于某种原因,它完全搞砸了实体的水合作用:
Unhandled Exception: NHibernate.PropertyAccessException: could not set a property value by reflection setter of Event.Translations ---> System.Reflection.TargetException: Object does not match target type.
at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invoke
Attr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisib
ilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invoke
Attr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, B
indingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, O
bject[] index)
at NHibernate.Properties.BasicPropertyAccessor.BasicSetter.Set(Object target,
Object value) in C:\thirdparty\NHibernate\src\NHibernate\Properties\BasicProper
tyAccessor.cs:line 304
在我看来,它不起作用,因为 NH 使用反射来设置属性,但当然,目标类型是错误的,因为我将 IEvent 换成了 Event。有办法解决这个问题吗?
另外,就基本方法而言,NH 有没有更好的方法来做到这一点?
I'm working on a problem in which I have a number of entities, each of which has a corresponding one-to-many translations table, which specify the localized versions of entity's fields. (all of this is legacy schema I'm adding a map on top of). For example:
- Event
- EventTranslation
- EventID
- Language
- Title
- other fields
So, if I were going to render my information in Greek, I'd join the two tables and specify Language = 'Greek' and have all the right stuff.
What I'm trying to do is build on-the-fly mixins that directly incorporate the right data into a single object and return that as the result of queries, so like:
var someEvent = session.CreateCriteria<Event>().SetMaxResults(1).UniqueResult<IEvent>();
Console.WriteLine(someEvent.Title);
To do that, I'm trying to set up an NHibernate interceptor to create DynamicProxy mixins. Except that it isn't working, and I don't know why. Here's the set up, as best as I could simplify it.
Here's Event:
class Event : IEventEntity {
//ID and props here
public IList Translations {get; set;}
}
IEvenEntity
also has a getter and setter for the list. There's also an EventTranslation
class, which is super simple and implements IEventTranslation
in the most obvious way possible.
The fluent map for Event:
class EventMap : ClassMap<Event>{
//obvious ID and properties stuff here...
HasMany<EventTranslation>(x => x.Translations);
}
Works fine on its own--I can query for events and navigate to their Translations. I'm pretty sure the mapping is good.
I based the shape of my interceptor stuff on a very cool guide by Krzysztof Koźmic for doing something slightly related. First, I created a superinterface that I'm going to implement with a dynamic proxy:
public interface IEvent : IEventEntity, IEventTranslation{}
Here's my NH interceptor. Obviously, I'm hacking like crazy:
public class EventInterceptor : NHibernate.EmptyInterceptor
{
private readonly static ProxyGenerator gen = new ProxyGenerator();
public override object Instantiate(string clazz, NHibernate.EntityMode entityMode, object id)
{
var mixin = gen.CreateClassProxy(typeof(object), new[] { typeof(IEvent) }, new DynInterceptor());
//would also need to set the identifier here
return mixin;
}
}
Where DynInterceptor is an interceptor that actually does the work of going into the relationships bag, getting the right translation, and returning the right value. The details aren't too relevant because it never gets called.
After tying in the NH interceptor, I see that it's running, and in fact NH gets the cast to IEvent
right (i.e. the proxy is at least being created). But for some reason, it totally screws up hydrating the entity:
Unhandled Exception: NHibernate.PropertyAccessException: could not set a property value by reflection setter of Event.Translations ---> System.Reflection.TargetException: Object does not match target type.
at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invoke
Attr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisib
ilityChecks)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invoke
Attr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, B
indingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, O
bject[] index)
at NHibernate.Properties.BasicPropertyAccessor.BasicSetter.Set(Object target,
Object value) in C:\thirdparty\NHibernate\src\NHibernate\Properties\BasicProper
tyAccessor.cs:line 304
It looks to me that it's not working because NH is using reflection to set the property, but of course, the target type is wrong because I swapped an IEvent in for the Event. Is there a way to get around this?
Also, as far as basic approach, is there a better way to do this in NH?
发布评论
评论(2)
好吧,对于我提出的这个问题有多复杂,事实证明它非常简单,如果我了解得足够多,我可以问这样的问题:“如何动态地使一个对象实现另一个接口而不丢失其类型? ”。答案是提供一个目标,如
OK, so for how complicated I made that question, it turns out to be pretty simple, and if I'd known enough, I could have asked the question as "how do I dynamically make an object implement another interface without losing its type?". The answer is to provide a target, as in
我不确定,但我认为您应该将过滤器与事件侦听器结合使用来获得您想要的结果。如果您在语言表上设置过滤器并使用事件侦听器在要启用过滤的事件之前启用过滤器,则可能会获得所需的行为。
I'm not sure, but I think you should use a filter in combination with an eventlistener to get the result you want. If you set a filter on your language table and use an eventlistener to enable the filter before the events you want to enable filtering you might get the desired behavior.