使用IQuerayble<TItem>
时,我们可以像这样致电Select
:
query.Select( item => new { A=item.Prop1, B=item.Prop2});
Select
方法需要Expression<Func<TItem,TResult>>
我需要使用ExpandoObject
而不是匿名但静态类型的类。
如果有可能,它看起来像:
query.Select( item => dynamic new ExpandoBoject { A=item.Prop1, B=item.Prop2});
所以我想构建表达式树Expression<Func<TItem,ExpandoObject>>
,其中对象的属性以与匿名类型类似的方式初始化。
只有初始化才需要动态功能,因此Func可以返回ExpandoObject
而不是dynamic
。
我找不到关于Expression.Dynamic
和我应该使用的相应粘合剂的大量文档。
更新1
为什么我需要这些东西?
因为我想要get primary keys
我想为任何实体类型做这件事。
我知道如何获取组成PK的属性列表,但是现在我需要将实体投影到EntityKey
。好吧,可能是同等级的。
var keys = context.Set<TEntity>().Where(Expression<Func<TEntity,bool>).Select(Expression<Func<TEntity,EntityKey>>);
正如我在评论中所提到的,包含块的lambdas无法转换为表达式树,所以我不能简单地创建字典并填充它。现在,我正在使用语义上接近此代码的表达式树:
var dict = new Dictionary<string,object>();
dict.Add("Prop1",value1);
dict.Add("Prop2",value2);
return dict
但我怀疑EF可以解析包含块的表达式。需要检查。
我很好奇它是否适用于动态对象和Expression.MemberInit,因为它适用于静态对象。
更新2
实体框架不支持字典初始化语法
它会向NotSupportedException
抛出消息: LINQ to Entities中仅支持具有单个元素的列表初始化项。
更新3
EF不支持块表达式。
带有消息的NotSupportedException
:“阻止”类型的未知LINQ表达式。
答案 0 :(得分:3)
现在我正在使用语义上接近此代码的表达式树:
var dict = new Dictionary<string,object>(); dict.Add("Prop1",value1); dict.Add("Prop2",value2); return dict;
您可以这样做,因为您可以将该代码编写为单个表达式,如下所示:
new Dictionary<string, object>
{
{ "Prop1", value1 },
{ "Prop2", value2 }
};
你可以创建一个包含这个表达式的表达式树(EF应该能够处理),如下所示:
var addMethod = typeof(Dictionary<string, object>).GetMethod("Add");
var expression = Expression.Lambda<Func<Dictionary<string, object>>>(
Expression.ListInit(
Expression.New(typeof(Dictionary<string, object>)),
Expression.ElementInit(
addMethod,
Expression.Constant("Prop1"),
value1Expression),
Expression.ElementInit(
addMethod,
Expression.Constant("Prop2"),
value2Expression)),
itemParameterExpression);
答案 1 :(得分:1)
所描述的事情很难,主要是因为我们无法在运行时动态创建匿名类型 - 它们需要在编译时才知道。所以我的主张是创建一个类,它可以包含任意选择类型的几个属性(类似于元组),但是我们将仅从对我们重要的属性加载db值。所以我们需要这样的课程:
public class CustomTuple<T1, T2>
{
public T1 Item1 { get; set; }
public T2 Item2 { get; set; }
}
如果我们需要更多,我们可以添加更多属性。如果我们将使用具有5个属性的类来使用它,我们可以加载最多5个属性。现在投影逻辑:
Type[] parameterTypes = new Type[] { typeof(int), typeof(object) };
Type tupleType = typeof(CustomTuple<,>).MakeGenericType(parameterTypes);
ParameterExpression x = Expression.Parameter(typeof(Entity));
NewExpression body = Expression.New(tupleType.GetConstructor(new Type[0]), new Expression[0]);
MemberBinding binding1 = Expression.Bind(
typeof(CustomTuple<,>).MakeGenericType(parameterTypes).GetProperty("Item1"),
Expression.Property(x, "Value"));
MemberInitExpression memberInitExpression =
Expression.MemberInit(
body,
binding1);
Expression<Func<Entity, object>> exp = Expression.Lambda<Func<Entity, object>>(memberInitExpression, x);
using (MyDbContext context = new MyDbContext())
{
var list = context.Entities.Select(exp).ToList();
}
上面的代码从实体类Value属性的Entities集合值中选择。 parameterTypes定义从Select投影返回的参数类型。如果我们不打算使用给定的属性,那么我们将其保留为对象类型。然后我们需要构建初始化表达式。我们使用方法New执行此操作,我们使用Expression.Bind创建的表达式分配属性值,并将它们与Expression.MemberInit组合在一起。我们可以在运行时动态创建尽可能多的MemberBinding表达式。