繁体   English   中英

如何在表达式树中实例化和初始化动态对象?

[英]How to instantiate and initialize a dynamic object in expression tree?

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

实体框架不支持字典初始化语法。
它会抛出带有消息的NotSupportedExceptionLINQ to Entities中仅支持具有单个元素的列表初始化项。


更新3

EF不支持块表达式。
NotSupportedException with message: 未知的“Block”类型的LINQ表达式。

现在,我正在使用语义上接近此代码的表达式树:

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

所描述的事情很难,主要是因为我们无法在运行时动态创建匿名类型 - 它们需要在编译时已知。 所以我的主张是创建一个类,它可以包含任意选择类型的几个属性(类似于元组),但是我们将仅从对我们重要的属性加载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表达式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM