ISession.Load(id) "Creating a proxy instance failed" "Sequence contains more than one matching element"

19 views Asked by At

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);
}
1

There are 1 answers

0
Mud On

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):

var typeConstraints = typeArg.GetGenericParameterConstraints()
      Select(x => ResolveTypeConstraint(method, x));

var baseTypeConstraint = typeConstraints.SingleOrDefault(x => x.IsClass);

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.