5

使用时,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

为什么我需要所有这些东西?
因为我想得到主键
我想为任何实体类型做这件事。

我知道如何获取组成 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 表达式。

4

2 回答 2

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);
于 2013-08-20T22:36:21.320 回答
1

所描述的事情很困难,主要是因为我们不能在运行时动态创建匿名类型——它们需要在编译时就已经知道。所以我的建议是创建一个类,该类可以包含多个任意选择类型的属性(类似于 Tuple),但是我们将从 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();
}

上面的代码从 Entity 类 Value 属性的 Entity 集合值中选择。parameterTypes 定义从 Select 投影返回的参数类型。如果我们不打算使用给定的属性,那么我们将其保留为对象类型。然后我们需要构建初始化表达式。我们使用 New 方法执行此操作,我们使用 Expression.Bind 创建的表达式分配属性值,并将它们与 Expression.MemberInit 组合在一起。我们可以在运行时根据需要动态创建尽可能多的 MemberBinding 表达式。

于 2014-07-23T20:36:19.480 回答