2011-03-03 40 views
12

我正在創建一個具有服務層(WCF網站)和Silverlight 4客戶端的應用程序。 RIA服務不是一種選擇,所以我們創建中介類來回傳遞。爲了這個問題的目的,讓我們假設我來回傳遞美味Food對象。實體框架選擇不帶.ToList()的新POCO

public class FoodData 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
    public Tastyness TastyLevel { get; set; } 
} 

的EF模型本質上是同一個班級,有三個基本字段(Tastyness是對應於我們的枚舉Tastyness一個int)的表。

我發現自己使用這種說法有很多做實體框架查詢時:

public List<FoodData> GetDeliciousFoods() 
{ 
    var deliciousFoods = entities.Foods 
           .Where(f => f.Tastyness == (int)Tastyness.Delicious) 
           .ToList() // Necessary? And if so, best performance with List, Array, other? 
           .Select(dFood => dFood.ToFoodData()) 
           .ToList(); 

    return deliciousFoods; 
} 

沒有.ToList()調用我得到一個異常有關LINQ不能夠自定義的方法轉換到查詢等價,我理解。

我的問題是關於調用.ToList()。選擇(...)與自定義擴展之前,我們的對象轉換爲食品對象的POCO版本。

是否有更好的模式可以在這裏執行,或者甚至可以更好地替代.ToList(),因爲我並不真的需要列表< ..> result的功能。

回答

11

使用ToListAsEnumerable的問題是您實現整個實體並支付修正費用。如果你想擁有最好的SQL僅返回所需的字段,那麼你應該直接投影,而不是使用.ToFoodData()

var deliciousFoods = entities.Foods 
          .Where(f => f.Tastyness == (int)Tastyness.Delicious) 
          .Select(dFood => new FoodData 
            { 
             Id = dFood.Id, 
             Name = dFood.Name, 
             TastyLevel = (Tastyness)dFood.Tastyness 
            }); 

來枚舉可能有問題的演員。如果是這樣,經過一個匿名類型:

var deliciousFoods = entities.Foods 
          .Where(f => f.Tastyness == (int)Tastyness.Delicious) 
          .Select(dFood => new FoodData 
            { 
             Id = dFood.Id, 
             Name = dFood.Name, 
             TastyLevel = dFood.Tastyness 
            }) 
          .AsEnumerable() 
          .Select(dFood => new FoodData 
            { 
             Id = dFood.Id, 
             Name = dFood.Name, 
             TastyLevel = (Tastyness)dFood.TastyLevel 
            }); 

如果您檢查生成的SQL,你會看到它的簡單,您不支付物體固定成的ObjectContext的成本。

+1

是的。你的第一個例子,直接投影,確實非常美味。 – 2011-03-03 23:08:16

+0

非常好,那正是我想要得到更好的解釋。在我的情況下,我使用數據傳輸對象(FoodData)中的對象的所有字段,所以SQL可能沒有太多改進。這個信息將決定我從現在開始如何查詢實體集合。 – Jacob 2011-03-04 01:28:59

+0

加1使用AsEnumerable() – mhand 2015-03-11 21:27:52

6

使用AsEnumerable()打開查詢到一個普通的舊LINQ到對象的查詢,而無需創建一個不需要列表

var deliciousFoods = entities.Foods 
           .Where(f => f.Tastyness == (int)Tastyness.Delicious) 
           .AsEnumerable() 
           .Select(dFood => dFood.ToFoodData()) 
           .ToList(); 

編輯:見http://www.hookedonlinq.com/AsEnumerableOperator.ashx

+0

您是否有任何數據可以支持更好的性能?陌生人在互聯網上? ;) – Jacob 2011-03-03 20:41:47

+0

@Jacob查看編輯 – cordialgerm 2011-03-03 20:45:17

+0

看起來很直截了當。我仍然想嘗試爲大數據集做一些實驗,看看時間的差異。 – Jacob 2011-03-03 20:54:55

0

第一.ToList()不要求。

var deliciousFoods = entities.Food 

    // Here a lazy-evaluated collection is created (ie, the actual database query 
    // has not been run yet) 
    .Where(f => f.Tastyness == (int)Tastyness.Delicious) 

    // With ToArray, the query is executed and results returned and 
    // instances of Food created. The database connection 
    // can safely be closed at this point. 
    // Given the rest of the linq query, this step can be skipped 
    // with no performance penalty that I can think of 
    .ToArray() 

    // Project result set onto new collection. DB Query executed if 
    // not already 
    // The above .ToArray() should make no difference here other 
    // than an extra function call an iteration over the result set 
    .Select(dFood => dFood.ToFoodData()) 

    // This one might not be needed, see below 
    .ToList(); 

您是否要求結果集爲列表<>?或者只是IEnumerable或ICollection是否足夠?如果是這樣,那麼可能不需要最後的.ToList()。

您問過關於性能?每個查詢會返回多少個實例?如果它相對較少,則.ToList()或.ToArray()或其他方法不會產生任何有意義的差異。它更多的是你需要公開什麼樣的功能?如果返回的對象需要是可索引的,可添加的,並且具有List的其他屬性,那沒關係。但是,如果您所做的只是遍歷返回的集合,請不要公開不需要的東西。

+0

ToArray比ToList更昂貴,因爲實現首先填充一個列表,然後從列表中創建一個數組。 – mhand 2015-03-11 21:28:56

1

@Craig Stuntz的答案是正確的,但是當您有多個查詢應該將'Food'對象轉換爲'FoodData'對象時可能會出現問題。您不希望表達式在多個位置重複(DRY)。

該解決方案可以沒有一個方法實際返回一個'FoodData'對象,但有一個方法返回用於進行轉換的表達式。然後您可以重新使用這種方法。

Class Food { 
    ... 

    public static Expression<Func<Food, FoodData> ConvertToFoodDataExpr() { 
    Expression<Func<Food, FoodData>> expr = dFood => new FoodData 
    { 
     Id = dFood.Id, 
     Name = dFood.Name, 
     TastyLevel = dFood.Tastyness 
    } 
    } 
} 

,並使用這個......

var deliciousFoods = entities.Foods 
         .Where(f => f.Tastyness == (int)Tastyness.Delicious) 
         .Select(Food.ConvertToFoodDataExpr()); 

使用實體框架時,你不應該兌現了IEnumerable記住(使用ToList,ToArray的等)befor應用選擇的表達,否則實體框架將無法制作正確的選擇語句,並始終選擇表中的所有字段。