簡體   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