Question

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
    • ID
  • 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?

Was it helpful?

Solution

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

Type type = Type.GetType(clazz, false);
var mixin = (Event)gen.CreateClassProxy(type, new[] { typeof(IEvent) }, new DynInterceptor());

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top