Hi All,
I ran into this same error and created a work around I would like to share.
The error, "Object does not match target type" that jbliss posted, is caused because the IList you are trying to bind has objects of two different types: your base data object and the proxy for your data object. This occurs when some of the objects you retrieve in your list had proxies created for them previously in the session. If you are unfamiliar with NHibernate proxies, review the documenation concerning Lazy Loading.
The actual error occurs because some of the GridView columns (such as HyperLinkColumn in this case) cache the PropertyDescriptor based on the first object in the list. Instead of checking to see if the property exists on the second type, the framework throws an Exception. IMHO it should have checked if the property requested existed on the second type and continued if it did.
There is really no good solution for this. Clearing the session will clear out all of the proxies that have been previously created and then when you perform your query no proxy objects will be returned. However, clearing the session is not always possible.
The work-around I created was to actually create a class that will wrap all of the objects in a DynamicProxy class created using Castle.DynamicProxy. This proxy uses reflection to reroute calls to the underlying object. This solves the bug because it returns a new list with objects of only one type: the newly created proxy wrapper.
It can be implemented simply for a list or set:
Code:
gridview.DataSource = SafeDataBindingList<Project>.Create(listOfNhibernateObjects);
gridview.DataSource = SafeDataBindingSet<Project>.Create(SetOfNhibernateObjects);
A separate list with proxy wrappers is only created if there are objects of more than one type in the list, otherwise the original list is returned. This way we only incur the performance penalty when we need to avoid this bug.
The code for these classes is below. You will need to reference the Castle.DynamicProxy assembly to compile.
Code:
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Castle.DynamicProxy;
namespace Interworks.NHibernateFramework
{
public class SafeDataBindingList<T> : List<T>
{
private IList<T> _innerList;
private SafeDataBindingList(IList<T> list) : base(list.Count)
{
_innerList = list;
ProxyGenerator generator = new ProxyGenerator();
foreach (T obj in list)
{
this.Add((T)generator.CreateClassProxy(typeof(T), new DataObjectInterceptor(obj)));
}
}
public IList<T> InnerList
{
get { return _innerList; }
}
/// <summary>
/// Creates a safe-wrapper around a list if needed for
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
public static IList<T> Create(IList<T> list)
{
if (list.Count <= 1)
return list;
string t1 = "";
string t2 = "";
t1 = list[0].GetType().ToString();
bool uniformList = true;
for (int i = 1; i < list.Count; i++)
{
t2 = list[i].GetType().ToString();
if (t1 == t2)
continue;
else
{
uniformList = false;
break;
}
}
if (uniformList)
return list;
return new SafeDataBindingList<T>(list);
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using Castle.DynamicProxy;
using Iesi.Collections.Generic;
namespace Interworks.NHibernateFramework
{
public class SafeDataBindingSet<T> : HashedSet<T>
{
private ISet<T> _innerSet;
private SafeDataBindingSet(ISet<T> set) : base()
{
_innerSet = set;
ProxyGenerator generator = new ProxyGenerator();
foreach (T obj in set)
{
this.Add((T)generator.CreateClassProxy(typeof(T), new DataObjectInterceptor(obj)));
}
}
public ISet<T> InnerSet
{
get { return _innerSet; }
}
/// <summary>
/// Creates a safe-wrapper around a list if needed for
/// </summary>
/// <param name="list"></param>
/// <returns></returns>
public static ISet<T> Create(ISet<T> set)
{
if (set.Count <= 1)
return set;
string t1 = "";
string t2 = "";
bool uniformSet = true;
foreach (T obj in set)
{
if (t1 == "")
{
t1 = obj.GetType().ToString();
continue;
}
t2 = obj.GetType().ToString();
if (t1 == t2)
continue;
else
{
uniformSet = false;
break;
}
}
if (uniformSet)
return set;
return new SafeDataBindingSet<T>(set);
}
}
}
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
using Castle.DynamicProxy;
namespace Interworks.NHibernateFramework
{
public class DataObjectInterceptor : IInterceptor
{
private static object _syncRoot = new object();
private static Dictionary<string, MethodInfo> _methodCache = new Dictionary<string, MethodInfo>();
private object _target = null;
public DataObjectInterceptor(object target)
{
_target = target;
}
public object Intercept(IInvocation invocation, params object[] args)
{
// it's possible that the type that the proxy was built on is not the type of
// the _target object. If it is, simply invoke MethodInfo, if it's not, we have
// to find the appropriate MethodInfo on the unknown type.
if (invocation.Method.ReflectedType.ToString() == invocation.InvocationTarget.GetType().ToString())
{
return invocation.Method.Invoke(_target, args);
}
else
{
MethodInfo method = GetMethod(invocation);
return method.Invoke(_target, args);
}
}
private MethodInfo GetMethod(IInvocation invocation)
{
Type targetType = _target.GetType();
string methodKey = GetMethodKey(targetType, invocation.Method.Name);
if (_methodCache.ContainsKey(methodKey))
return _methodCache[methodKey];
else
{
MethodInfo methodInfo = null;
if (invocation.Method.Name.StartsWith("get_") || invocation.Method.Name.StartsWith("set_"))
methodInfo = GetPropertyMethod(targetType, invocation);
else
{
methodInfo = GetMethod(targetType, invocation.Method.Name);
}
lock (_syncRoot)
{
if (methodInfo != null && !_methodCache.ContainsKey(methodKey))
_methodCache.Add(methodKey, methodInfo);
}
return methodInfo;
}
}
private MethodInfo GetMethod(Type targetType, string methodName)
{
// search class hierarchy for method
Type type = targetType;
do
{
// Use DeclaredOnly to see if we can get the method directly from this type
MethodInfo method = type.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
if (method != null)
return method;
type = type.BaseType;
} while (type != null);
return null;
}
private MethodInfo GetPropertyMethod(Type targetType, IInvocation invocation)
{
string propertyName = invocation.Method.Name.Split('_')[1];
bool isGet = invocation.Method.Name.StartsWith("get_");
Type type = targetType;
PropertyInfo propertyInfo = null;
do
{
propertyInfo = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly);
if (propertyInfo != null)
break;
type = type.BaseType;
} while (type != null);
if (propertyInfo == null)
return null;
if (isGet)
return propertyInfo.GetGetMethod();
else
return propertyInfo.GetSetMethod();
}
private string GetMethodKey(Type targetType, string methodName)
{
return targetType.ToString() + "." + methodName;
}
}
}