使用带有动态匿名对象的NHibernate在GroupBy中进行选择 [英] Selection in GroupBy query with NHibernate with dynamic anonymous object

查看:249
本文介绍了使用带有动态匿名对象的NHibernate在GroupBy中进行选择的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的主要目标是创建一个动态组,并在NHibernate中使用它.

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

现在,我创建一个动态对象来动态生成new { u.Active }部分:

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

类型是在方法GetDynamicTypeForGroup中生成的,然后用Expression.MemberInit(Expression.New(dynamicTypeForGroup.GetConstructor(Type.EmptyTypes)), bindings)

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

这是类型的生成方式:

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


所以,问题

如果我使用_repository.Collection<User>().GroupBy(u => new { u.Active }),它会起作用,但是如果我添加Select部分-.Select(s => s.Key, Count = s.Count())(或任何选择语句),则会得到以下 NotSupportedException:MemberInit


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(表达式)(省略)

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

我的疑问是:

  • 我知道NHibernate支持带有 group by 和匿名类型的Select语句,但是如果这种类型是用表达式树创建的,为什么它不能支持Select?
  • 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?

推荐答案

显然,NHibernate LINQ查询转换器不支持GroupBy选择器中的MemberInitExpression.

Apparently NHibernate LINQ query translator does not support MemberInitExpression in GroupBy selector.

但是为什么匿名类型有效?因为尽管表达式new { Active = u.Active }在语法上看起来像 MemberInitExpression(类初始值设定项),但实际上不是!

But why anonymous type works? Because although expression new { Active = u.Active } syntactically looks like MemberInitExpression (class initializer), in fact it isn't!

C#编译器生成的(和NHibernate支持的)是对带有参数的构造函数的调用(成员属性-以下

What C# compiler generates (and NHibernate supports) is a call to a constructor with parameters (NewExpression) mapped to class members through Members property - the third argument of the following Expression.New overload:

public static NewExpression New (
    ConstructorInfo constructor,
    IEnumerable<Expression> arguments,
    IEnumerable<MemberInfo> members
)

这是问题的解决方案.在动态类型生成器中,生成一个参数与字段匹配的构造器(并在体内分配相应的字段):

Which is the solution of the problem. Inside your dynamic type builder, generate a constructor with parameters matching the fields (and assign the corresponding fields inside the body):

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

GenerateConstructor(typeBuilder, fields); // <--

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

return typeBuilder.CreateType();

,然后使用类似这样的内容:

and then use something like this:

private Expression<Func<T, object>> CreateGrouping<T>(IEnumerable<string> by)
{
    var keyType = GetDynamicTypeForGroup<T>(by);
    var sourceItem = Expression.Parameter(typeof(T));
    var members = keyType.GetFields();
    var arguments = members.Select(m => Expression.PropertyOrField(sourceItem, m.Name));
    var constructor = keyType.GetConstructor(members.Select(m => m.FieldType).ToArray());
    var newKey = Expression.New(constructor, arguments, members);        
    return Expression.Lambda<Func<T, object>>(newKey, sourceItem);
} 

这篇关于使用带有动态匿名对象的NHibernate在GroupBy中进行选择的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆