我正在创建一个带有服务层(WCF网站)和Silverlight 4客户端的应用程序。 RIA服务不是一种选择,因此我们创建了来回传递的中间类。出于这个问题的目的,让我们假设我来回传递美味Food
对象。
public class FoodData
{
public int Id { get; set; }
public string Name { get; set; }
public Tastyness TastyLevel { get; set; }
}
EF模型本质上是同一个类,一个包含三个基本字段的表(Tastyness是一个与我们的enum 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无法将自定义方法转换为等价查询的异常,我理解。
我的问题是在 .Select(...)之前调用 .ToList()并使用自定义扩展将我们的对象转换为POCO版本食物对象。
这里有更好的模式,或者甚至可能是更好的替代.ToList()可能更高效,因为我并不真正需要List&lt; ..&gt;的功能结果。
答案 0 :(得分:11)
使用ToList
或AsEnumerable
的问题在于您实现了整个实体并支付了修复费用。如果你想拥有最好的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 :(得分:6)
使用AsEnumerable()
将查询转换为常规的旧LINQ to Objects查询,而无需创建不需要的列表
var deliciousFoods = entities.Foods
.Where(f => f.Tastyness == (int)Tastyness.Delicious)
.AsEnumerable()
.Select(dFood => dFood.ToFoodData())
.ToList();
答案 2 :(得分: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());
请记住,在使用Entity Framework时,您不应该实现IEnumerable(使用ToList,ToArray等)来应用select表达式,否则Entity Framework将无法生成正确的select语句,它将始终选择表中的所有字段。
答案 3 :(得分: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();
您是否要求结果集为List&lt;&gt;?或者只是IEnumerable或ICollection足够吗?如果是这样,则可能不需要最后一个.ToList()。
你问过性能?您希望每个查询返回多少个实例?如果它相对较少,则.ToList()或.ToArray()或其他不会产生任何有意义的差异。它更多的是您需要公开哪种功能?如果返回的对象需要是可索引的,可添加的,并且具有List的其他属性,那没关系。但是,如果您所做的只是迭代返回的集合,请不要暴露不需要的内容。