NHibernate proxy causing problems with databinding

2019-02-25 01:22发布

问题:

I have a gridview that is bound to the result from an nhibernate query. If the first item in the list is edited the following exception is thrown:

System.Reflection.TargetException: Object does not match target type

It appears that the problem is caused by the fact that databinding can't deal with the first item in the list being a subtype of the other items in the list.

What is a nice / correct way to solve this problem? Currently I have had to turn off nhibernates proxying.

Edit: I have another couple of solutions:

  • Clone everything in the list (http://steve-fair-dev.blogspot.com/2007/08/databind-object-does-not-match-target.html) - this doesn't work for me as the object doesn't implement ICloneable
  • change the order of items in the list so that the proxy isn't first (http://community.devexpress.com/forums/t/30797.aspx) - this is so hacky, I don't think I can stoop this low!

But none of these feel right though...

回答1:

Is the root cause due to a proxy object in the list (from lazy loading) or because the list isn't homogeneous (contains multiple types even if they belong to the same class hierarchy)? The problem with non-homogeneous data sets is a known limitation. See this and this.

I don't think there's a solution other than to not use databinding to populate the grid. That's easy enough if it's read-only.



回答2:

Maybe too late, but I'd just like to throw this into the ring, here is a solution that I've used for this.

It is also called 'SafeBindingList' like the other suggestion above, but, it does not 'clone' objects to solve the problem. It looks at the objects in the list, then if none proxied, the list is returned unmodified. If one or more objects are proxied, it adds an empty proxy to the non-proxied objects, thus making them all the same type.

So, instead returning a List[T] to bind to, use SafeBindingList[T] to ensure all objects have the same type.

This is updated for the version of Castle used with NH2.0.1: http://code.google.com/p/systembusinessobjects/source/browse/trunk/System.BusinessObjects.Framework/Data/SafeBindingLists.cs

Also, credit goes to the original code and poster: https://forum.hibernate.org/viewtopic.php?t=959464&start=0&postdays=0&postorder=asc&highlight=



回答3:

I don't use my domain objects in directly in the views. Instead I use the MVVM pattern and create suitable view models that holds non-proxied objects.



回答4:

Another solution is to Join Fetch the relation if you know you are going to be Databinding it. E.g. add .SetFetchMode("People", FetchMode.Join). NHibernate should return only domain objects since none of them should be lazy loaded.



回答5:

Very late, but should help others with the same problem. The solution I used is to wrap a custom list (in this case a NotificationList) around the field in the getter.

private IList<IParameter> _parameters = new List<IParameter>();  
get  
{  
    return new NotificationList<IParameter>(_parameters);  
}

This list is a wrapper around the list, so that databinding will be forwarded to the original list.

public class NotificationList<T> : IList, IList<T>    
{
    IList<T> myList;
    public NotificationList(IList<T> list)
    {
        myList = list;
    }
    int IList.Add(object item)
    {
        myList.Add ((T) item);
    } 
    // implement both IList<T> and IList
    // ...
}

For me this fixed the issue with the databinding, but created a side effect where every time the session was flushed all the items in the collection get updated in the DB, wether they changed or not. To resolve that, I changed the mapping to access the field directly. See this on Hibernate, which applies to NHibernate as well.
This is the new (Fluent) mapping:

HasMany(x => x.Parameters)
       .Cascade.All()
       .Access.CamelCaseField(Prefix.Underscore);