[英]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
實體框架不支持字典初始化語法。
它會拋出帶有消息的NotSupportedException
: LINQ 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.