使用带有动态匿名对象的NHibernate在GroupBy中进行选择 [英] Selection in GroupBy query with NHibernate with dynamic anonymous object
问题描述
我的主要目标是创建一个动态组,并在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屋!