[英]Dynamically build select list from linq to entities query
我正在尋找一種從iQueryable對象動態創建選擇列表的方法。
具體的例子,我想做類似以下的事情:
public void CreateSelectList(IQueryable(of EntityModel.Core.User entities), string[] columns)
{
foreach(var columnID in columns)
{
switch(columnID)
{
case "Type":
SelectList.add(e => e.UserType);
break;
case "Name":
SelectList.add(e => e.Name);
break;
etc....
}
}
var selectResult = (from u in entities select objSelectList);
}
因此,所有屬性都是已知的,但我事先並不知道要選擇哪些屬性。 這將通過columns參數傳遞。
我知道我將遇到selectResult類型的問題,因為當選擇列表是動態的時,編譯器不知道匿名類型的屬性需要是什么。
如果上述情況不可能:我需要的方案如下:
我正在嘗試創建一個可以實現的類來顯示分頁/過濾的數據列表。 這些數據可以是任何東西(取決於實現)。使用的linq是實體的linq。 所以它們直接鏈接到sql數據。 現在我只想選擇我實際在列表中顯示的實體的列。 因此我希望select是動態的。 我的實體可能有一百個屬性,但如果列表中只顯示了3個屬性,我不想生成一個選擇所有100列數據的查詢,然后只使用其中的3個。 如果有一種我沒有想到的不同方法,我會接受各種想法
編輯:
關於約束的一些澄清:
- 查詢需要與linq一起使用實體(參見問題主題)
- 一個實體可能包含100列,因此選擇所有列然后只讀取我需要的列不是一個選項。
- 最終用戶決定顯示哪些列,因此要在運行時確定要選擇的列
- 我需要創建一個SINGLE選擇,有多個select語句意味着在數據庫上有多個查詢,這是我不想要的
動態選擇表達式到編譯時已知類型可以使用Expression.MemberInit
方法輕松構建,其中MemberBinding
是使用Expression.Bind
方法創建的。
這是一個自定義擴展方法,它執行以下操作:
public static class QueryableExtensions
{
public static IQueryable<TResult> Select<TResult>(this IQueryable source, string[] columns)
{
var sourceType = source.ElementType;
var resultType = typeof(TResult);
var parameter = Expression.Parameter(sourceType, "e");
var bindings = columns.Select(column => Expression.Bind(
resultType.GetProperty(column), Expression.PropertyOrField(parameter, column)));
var body = Expression.MemberInit(Expression.New(resultType), bindings);
var selector = Expression.Lambda(body, parameter);
return source.Provider.CreateQuery<TResult>(
Expression.Call(typeof(Queryable), "Select", new Type[] { sourceType, resultType },
source.Expression, Expression.Quote(selector)));
}
}
唯一的問題是什么是TResult
類型。 在EF Core中,您可以傳遞實體類型(如EntityModel.Core.User
中的EntityModel.Core.User
),它將起作用。 在EF 6及更早版本中,您需要一個單獨的非實體類型,否則您將獲得NotSupportedException
- 無法在LINQ to Entities查詢中構造實體或復雜類型 。
更新:如果你想要刪除字符串列,我建議你用以下類替換擴展方法:
public class SelectList<TSource>
{
private List<MemberInfo> members = new List<MemberInfo>();
public SelectList<TSource> Add<TValue>(Expression<Func<TSource, TValue>> selector)
{
var member = ((MemberExpression)selector.Body).Member;
members.Add(member);
return this;
}
public IQueryable<TResult> Select<TResult>(IQueryable<TSource> source)
{
var sourceType = typeof(TSource);
var resultType = typeof(TResult);
var parameter = Expression.Parameter(sourceType, "e");
var bindings = members.Select(member => Expression.Bind(
resultType.GetProperty(member.Name), Expression.MakeMemberAccess(parameter, member)));
var body = Expression.MemberInit(Expression.New(resultType), bindings);
var selector = Expression.Lambda<Func<TSource, TResult>>(body, parameter);
return source.Select(selector);
}
}
樣本用法:
var selectList = new SelectList<EntityModel.Core.User>();
selectList.Add(e => e.UserType);
selectList.Add(e => e.Name);
var selectResult = selectList.Select<UserDto>(entities);
你想要的是可能的,但這並不簡單。 您可以使用System.Linq.Expressions命名空間中的方法和類動態構建EF查詢。
有關如何動態構建Select表達式的一個很好的示例,請參閱此問題 。
我相信這就是你所需要的:
var entities = new List<User>();
entities.Add(new User { Name = "First", Type = "TypeA" });
entities.Add(new User { Name = "Second", Type = "TypeB" });
string[] columns = { "Name", "Type" };
var selectResult = new List<string>();
foreach (var columnID in columns)
{
selectResult.AddRange(entities.Select(e => e.GetType().GetProperty(columnID).GetValue(e, null).ToString()));
}
foreach (var result in selectResult)
{
Console.WriteLine(result);
}
此代碼輸出:
- 第一
- 第二
- 類型A
- 的TypeB
更新(根據評論)
// initialize alist of entities (User)
var entities = new List<User>();
entities.Add(new User { Name = "First", Type = "TypeA", SomeOtherField="abc" });
entities.Add(new User { Name = "Second", Type = "TypeB", SomeOtherField = "xyz" });
// set the wanted fields
string[] columns = { "Name", "Type" };
// create a set of properties of the User class by the set of wanted fields
var properties = typeof(User).GetProperties()
.Where(p => columns.Contains(p.Name))
.ToList();
// Get it with a single select (by use of the Dynamic object)
var selectResult = entities.Select(e =>
{
dynamic x = new ExpandoObject();
var temp = x as IDictionary<string, Object>;
foreach (var property in properties)
temp.Add(property.Name, property.GetValue(e));
return x;
});
// itterate the results
foreach (var result in selectResult)
{
Console.WriteLine(result.Name);
Console.WriteLine(result.Type);
}
此代碼輸出:
- 第一
- 類型A
- 第二
- 的TypeB
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.