2013-04-11 46 views
1

我有以下查詢:限制查詢基於在運行時設置列表的列

// Type T is constrained to a class that contains "ID" property 
// propertiesToQuery is a list constructed based on type T 
var set = AppContext.Set<T>(); 
var result = set.SelectMany(x => propertiesToQuery.Select(p => new { x.ID, Value = x.GetType().GetProperty(p.Name).GetValue(x) }) 
                  .Where(p => p.Value != null) 
                  .Select(p => new SearchIndexItem 
                      { 
                       Key = p.Value.ToString(), 
                       Url = Url.Action("Edit", type.Name, new { p.ID }), 
                       Type = type 
                      })); 

現在因爲LINQ到實體不允許在查詢中使用的PropertyInfo,我需要運行一個ToList()在集爲了首先在db上執行查詢,然後執行所需的SelectMany()。

從數據庫中查詢超過它需要的數據量,當數據量很大時(查詢的列是字符串類型的,其他的可以是blob,這些是我不想拉的數據庫數據從)

所以問題是我怎麼能限制列基於在運行時構造的列表查詢數據庫?

我試圖創建一個表達式樹,並把它傳遞到機上的Select()方法,但問題是與創建匿名類型,它可以在T類型

回答

1

不同depenging你觀察這裏:

的問題是與在創建匿名類型,其可以是對類型T不同depenging

準確;在運行時構造是非常有問題的。只有簡單的方式做到這一點是要使它看起來像你填充一個投影具有所有成員的類型,例如相當於:

// where our list is "Foo", "Bar": 
x => new SomeType { 
    Foo = x.Foo, 
    Bar = x.Bar 
    // but other SomeType properties exist, but are't mapped 
} 

明顯的競爭者將是實體類型,因此您將一組Customer行部分映射到Customer對象 - 但大多數ORM不會讓您這樣做:如果投影是實體類型,則需要整個類型(即x => x)。你也許可以創建是一個普通的POCO/DTO,但不是實體模型的一部分實體類型的第二個版本,即

Customer x => new CustomerDto { 
    Foo = x.Foo, 
    Bar = x.Bar 
} 

,你可以做在運行時的Expression.MemberInit一部分。例如:

class Foo 
{ 
    public string A { get; set; } 
    public int B { get; set; } 
    public DateTime C { get; set; } 
} 
class FooDto 
{ 
    public string A { get; set; } 
    public int B { get; set; } 
    public DateTime C { get; set; } 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 
     var data = new[] { new Foo { A = "a", B = 1, C = DateTime.Now}} 
       .AsQueryable(); 
     var mapped = PartialMap<Foo, FooDto>(data, "A", "C").ToList(); 
    } 
    static IQueryable<TTo> PartialMap<TFrom, TTo>(
     IQueryable<TFrom> source, params string[] members) 
    { 
     var p = Expression.Parameter(typeof(TFrom)); 
     var body = Expression.MemberInit(Expression.New(typeof(TTo)), 
      from member in members 
      select (MemberBinding)Expression.Bind(
       typeof(TTo).GetMember(member).Single(), 
       Expression.PropertyOrField(p, member)) 
      ); 
     return source.Select(Expression.Lambda<Func<TFrom, TTo>>(body, p)); 
    } 
} 

在輸出中,和AC值,但B沒有。

+0

哇,這是一個有趣的方法。我可以使用代理/適配器/裝飾器而不是dto類型,或者將實體框架檢測到繼承並拋出錯誤嗎? – Chris 2013-04-11 11:18:10

+0

@Chris不知道 - 如果你知道,請告訴我 – 2013-04-11 11:29:42

+0

不幸的是,這是不可能的:/但非常感謝這個想法,我認爲我應該能夠根據自己的需要進行調整。 – Chris 2013-04-11 11:55:47