I'm upgrading an app from a very old version of NHibernate (2.1 to 5.5). The app builds and runs and it's reading/writing entities from/to the database.
However, when calling ISession.Load I get the following exception:
Creating a proxy instance failed
at NHibernate.Proxy.StaticProxyFactory.GetProxy(Object id, ISessionImplementor session)
at NHibernate.Event.Default.DefaultLoadEventListener.CreateProxyIfNecessary(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options, IPersistenceContext persistenceContext)
at NHibernate.Event.Default.DefaultLoadEventListener.ProxyOrLoad(LoadEvent event, IEntityPersister persister, EntityKey keyToLoad, LoadType options)
at NHibernate.Event.Default.DefaultLoadEventListener.OnLoad(LoadEvent event, LoadType loadType)
at NHibernate.Impl.SessionImpl.FireLoad(LoadEvent event, LoadType loadType)
at NHibernate.Impl.SessionImpl.Load(String entityName, Object id)
at NHibernate.Impl.SessionImpl.Load[T](Object id)
INNER EXCEPTION:
Sequence contains more than one matching element
at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
at NHibernate.Proxy.ProxyBuilderHelper.GenerateMethodSignature(String name, MethodInfo method, TypeBuilder typeBuilder)
at NHibernate.Proxy.NHibernateProxyBuilder.ImplementCallMethodOnImplementation(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField, Type parentType)
at NHibernate.Proxy.NHibernateProxyBuilder.CreateProxiedMethod(TypeBuilder typeBuilder, MethodInfo method, FieldInfo lazyInitializerField, Type parentType)
at NHibernate.Proxy.NHibernateProxyBuilder.CreateProxyType(Type baseType, IReadOnlyCollection`1 baseInterfaces)
at NHibernate.Proxy.StaticProxyFactory.CreateProxyActivator(ProxyCacheEntry pke)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at NHibernate.Proxy.StaticProxyFactory.GetProxy(Object id, ISessionImplementor session)
I looked at the source code of NHibernate's ProxyBuilderHelper to see where SingleOrDefault is called in GenerateMethodSignature, and it's here:
var typeArgs = method.GetGenericArguments();
if (typeArgs.Length > 0)
{
var typeNames = GenerateTypeNames(typeArgs.Length);
var typeArgBuilders = methodBuilder.DefineGenericParameters(typeNames);
for (var index = 0; index < typeArgs.Length; index++)
{
// Copy generic parameter attributes (Covariant, Contravariant, ReferenceTypeConstraint,
// NotNullableValueTypeConstraint, DefaultConstructorConstraint).
var typeArgBuilder = typeArgBuilders[index];
var typeArg = typeArgs[index];
typeArgBuilder.SetGenericParameterAttributes(typeArg.GenericParameterAttributes);
// Copy generic parameter constraints (class and interfaces).
var typeConstraints = typeArg.GetGenericParameterConstraints()
.ToArray(x => ResolveTypeConstraint(method, x));
var baseTypeConstraint = typeConstraints.SingleOrDefault(x => x.IsClass);
typeArgBuilder.SetBaseTypeConstraint(baseTypeConstraint);
var interfaceTypeConstraints = typeConstraints.Where(x => !x.IsClass).ToArray();
typeArgBuilder.SetInterfaceConstraints(interfaceTypeConstraints);
}
}
It's getting the set of constraints on the generic type parameter, then trying to find contraint with the "IsClass == true", and apparently there is more than one. I have no clue what this means or how to fix it.
This is the calling method. The type constraint is for a class:
public TEntity GetProxy<TEntity>(Guid id) where TEntity : Entity
{
...
return Session.Load<TEntity>(id);
}
One of the methods of my Entity class had a subclass type constraint with more than one target class, e.g.
protected virtual SomeMethod<T>(T arg): where T: Foo, Bar.Not the comma-separated list after the
:. NHibernate chokes on that, because of this code in the proxy generation code (GenerateMethodSignature() in ProxyBuilderHelper.cs):There is more than one type constraint with IsClass == true, so SingleOrDefault throws.
In this case, I was able to remove the second type constraint and the problem was fixed.