I'm creating an application with a service layer (WCF Website) and a Silverlight 4 Client. RIA Services are not an option, so we create intermediary classes to pass back and forth. For the purpose of this question let's assume I'm passing back and forth Tasty Food
Objects.
public class FoodData
{
public int Id { get; set; }
public string Name { get; set; }
public Tastyness TastyLevel { get; set; }
}
The EF Model is essentially the same class, a table with three basic fields (the Tastyness is an int that corresponds to our enum Tastyness).
I find myself using this kind of statement a lot when doing Entity Framework queries:
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;
}
Without the .ToList() call I get an exception about LINQ not being able to translate the custom method to a query equivalent, which I understand.
My question is about the call to .ToList() before the .Select(...) with the custom extension to convert our object to the POCO version of the Food object.
Is there a better pattern to do here, or maybe even a better alternative to .ToList() that may be more performant since I don't really require the functionality of the List<..> result.
The problem with using ToList
or AsEnumerable
is that you materialize the entire entity and pay the cost of fixup. If you want to have the best possible SQL which returns only the needed fields, then you should project directly rather than using .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
});
The cast to enum may be a problem. If so, go through an anonymous type:
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
});
If you examine the resulting SQL, you'll see it's simpler, and you don't pay the cost of fixing up objects into the ObjectContext.
Use AsEnumerable()
to turn the query into a regular old LINQ to Objects query without having to create an unneeded List
var deliciousFoods = entities.Foods
.Where(f => f.Tastyness == (int)Tastyness.Delicious)
.AsEnumerable()
.Select(dFood => dFood.ToFoodData())
.ToList();
Edit: See http://www.hookedonlinq.com/AsEnumerableOperator.ashx
The answer of @Craig Stuntz is correct, however there could be an issue when you have multiple queries that should transform a 'Food' object into a 'FoodData' object. You don't want the expression to be duplicated in multiple locations (DRY).
The solution can be to not have a method that actually returns a 'FoodData' object, but to have a method that returns the expression to be used to make the transformation. You can then re-use this method.
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
}
}
}
And to use this...
var deliciousFoods = entities.Foods
.Where(f => f.Tastyness == (int)Tastyness.Delicious)
.Select(Food.ConvertToFoodDataExpr());
Remember when using Entity Framework, that you shouldn't materialize the IEnumerable (using ToList, ToArray etc.) befor applying the select expression, otherwise Entity Framework won't be able to make a correct select statement and it will always select all fields from the table.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With