c# - 使用带有动态匿名对象的 NHibernate 在 GroupBy 查询中进行选择

标签 c# .net reflection nhibernate reflection.emit

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

考虑这个有效的非动态示例:

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

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

    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) 实例化

这是生成类型的方式:

    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(s => s.Key, Count = s.Count()) (或任何选择语句)我得到以下 NotSupportedException: MemberInit

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

我的疑问是:

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

最佳答案

显然 NHibernate LINQ 查询翻译器不支持 GroupBy 选择器中的 MemberInitExpression

但为什么匿名类型有效?因为虽然表达式 new { Active = u.Active } 在语法上看起来 MemberInitExpression(类初始化器),但实际上它不是!

C# 编译器生成的(NHibernate 支持的)是对构造函数的调用,其参数 ( NewExpression ) 通过 Members 映射到类成员属性 - 以下 Expression.New 的第三个参数过载:

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

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

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

然后使用这样的东西:

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

关于c# - 使用带有动态匿名对象的 NHibernate 在 GroupBy 查询中进行选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56079838/

相关文章:

c# - 将值添加到行、检索它们并拆分它们

c# - 我正在使用反射来获取 ASP.NET 中的属性名称和值需要一些优化建议

c#获取接口(interface)方法注释

reflection - 如何从字符串中获取有区别的联合案例?

c#:两个双数之和问题

c# - 如何发送 Microsoft Graph 上传 session 的最终字节?

c# - log4net:未调用自定义 PatternLayoutConverter

c# - Java/.NET 中的 RSA 加密和 .NET 中的解密

c# - C# 中是否存在 else if 语句?

c# - DbC(按契约(Contract)设计)和单元测试