vendredi 10 mai 2019

Selection in GroupBy query with NHibernate with dynamic anonymous object

My main objective is to create a dynamic group by and use it in NHibernate.

Consider this non dynamic example that works:

_repository.Collection<User>().GroupBy(u => new { u.Active }).Select(s => s.Key, Count = s.Count())

Now, I create a dynamic object to generate the new { u.Active } part dynamically:

    private Expression<Func<T, object>> CreateGrouping<T>(IEnumerable<string> by)
    {
        var dynamicTypeForGroup = GetDynamicTypeForGroup<T>(by);
        var sourceItem = Expression.Parameter(typeof(T));

        var bindings = dynamicTypeForGroup
            .GetFields()
            .Select(p => Expression.Bind(p, Expression.PropertyOrField(sourceItem, p.Name)))
            .Cast<MemberBinding>()
            .ToArray();
        return Expression.Lambda<Func<T, object>>(Expression.Convert(
            Expression.MemberInit(
                Expression.New(dynamicTypeForGroup.GetConstructor(Type.EmptyTypes)),
                bindings),
            dynamicTypeForGroup),
        sourceItem);
    }

The type is generated in method GetDynamicTypeForGroup and then instantiated with Expression.MemberInit(Expression.New(dynamicTypeForGroup.GetConstructor(Type.EmptyTypes)), bindings)

This is how the Type is generated:

    private Type GetDynamicTypeForGroup<T>(IEnumerable<string> members)
    {
        var type = typeof(T);
        var dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
            new AssemblyName(Guid.NewGuid().ToString()),
            AssemblyBuilderAccess.RunAndSave
        );
        var dynamicModule = dynamicAssembly.DefineDynamicModule(Guid.NewGuid().ToString());
        var typeBuilder = dynamicModule.DefineType(Guid.NewGuid().ToString());

        var properties = members.Select(prop => type.GetProperty(ObjectExtensions.NormilizePropertyName(prop)))
                            .Where(prop => prop != null)
                            .Cast<MemberInfo>();

        var fields = properties
            .Select(property => typeBuilder.DefineField(
                property.Name,
                ((PropertyInfo)property).PropertyType,
                FieldAttributes.Public
            )).Cast<FieldInfo>()
            .ToArray();

        GenerateEquals(typeBuilder, fields);
        GenerateGetHashCode(typeBuilder, fields);

        return typeBuilder.CreateType();
    }


SO, THE PROBLEM

If I use _repository.Collection<User>().GroupBy(u => new { u.Active }) it works, but if I add the Select part - .Select(s => s.Key, Count = s.Count()) (or any select statment) I got the following NotSupportedException: MemberInit

System.NotSupportedException: MemberInit em NHibernate.Linq.Visitors.HqlGeneratorExpressionVisitor.VisitExpression(Expression expression) (ommited)

My doubt is:

  • I know that NHibernate supports a Select statement with a group by and an anonymous type, but why it can't suport the Select if this type was created with the Expression Tree?




Aucun commentaire:

Enregistrer un commentaire