I have a database and an Entity Framework model mapped to it. The database has a table "Products" with numerous columns. In many EF queries I need only a few columns, and I do a projection, let's say
var projected = Context.Products
.Select(p => new ProjectedProduct { ProdID = p.ID, ProdTitle = p.Title })
.ToArray();
Since this projection is used many times, I move it to a separate method:
public static IQueryable<ProjectedProduct> ToProjectedProduct(this IQueryable<Product> query)
{
return query.Select(p =>
new ProjectedProduct { ProdID = p.ID, ProdTitle = p.Title });
}
So I can use the projection like:
var projected = Context.Products.ToProjectedProduct().ToArray();
Now I also want to use the same projection for a single instance of product, like:
var prod = Context.Products.First(p => p);
var projected = new ProjectedProduct { ProdID = prod.ID, ProdTitle = prod.Title });
And I still want to use the same helper method for the projection in order to have it in one place, but it won't work, because it works only for IQueryable. What I can do is convert the projection to another method like
public static ProjectedProduct ToProjectedProduct(this Product p)
{
return new ProjectedProduct { ProdID = p.ID, ProdTitle = p.Title });
}
But now this method won't work for IQueryable. I need a helper method which would work for both cases, what I would want to do is:
var projected = Context.Products.Select(p => p.ToProjectedProduct()).ToArray();
But this doesn't work because the helper method can't be translated to the database query.
The select extension method of an IQueryable requires an Expression<Func<TSource, TResult>>
. What this means is we can define an object elsewhere that matches this parameter and pass it into our select statement.
Property (could also be a method):
public Expression<Func<Product, ProjectedProduct>> MapProduct
{
get
{
return p => new ProjectedProduct { ProdID = p.ID, ProdTitle = p.Title };
}
}
IQueryable usage:
var projected = Context.Products
.Select(MapProduct)
.ToArray();
Single instance usage:
var projected = Context.Products.Select(MapProduct).First();
IEnumerable usage:
// calling compile turns the expression into a normal Func
var projected = ProductList.Select(MapProduct.Compile());
Normal Object usage:
var projected = MapProduct.Compile()(ProdObj);
MSDN IQueryable.Select - http://msdn.microsoft.com/en-us/library/bb534638(v=vs.100).aspx