I’m still fairly new to Entity Framework (currently developing using v4.1), so I may be going about this in completely the wrong way, so I’d welcome any guidance.
I currently have two classes: Item which just has a few simple fields, and FavouriteItem which holds a reference to an item and a user (but the user relationship isn’t relevant to the discussion). The item has a nullable DateVisible field which I’m using as a flag to indicate if an item should be displayed or not.
In order to get a list of visible items, I’m using a simple query:
var items = itemRepository
.QueryAll()
.Where(i => i.DateVisible <= DateTime.Now)
Where obviously QueryAll returns an IQueryable. This is a straightforward example, as the conditions are a little more complex, but it demonstrates my issue.
Next, if I want to return a list of favourites for items which are visible, then I can do a similar query:
var favourites= favouriteRepository
.QueryAll()
.Where(f => f.Item.DateVisible <= DateTime.Now)
This works fine. However, the code is duplicated. So what I’d like to do is somehow make this code more central – as I’m about to add a FollowingItem class which again has an Item property, and so I’d have to repeat the code again – not good.
I started to create an expression for the list of items, which works well:
public static Expression<Func<Item, bool>> IsVisibleExpression<Item>()
{
return item => item.DateVisible != null &&
item.DateVisible <= DateTime.Now;
}
public static IQueryable<Item> WhereVisible<Item>(this IQueryable<Item> queryable)
{
return queryable.Where(Item.IsVisibleExpression());
}
var items = itemRepository
.QueryAll()
.WhereVisible()
And again, I can create these expressions for each class:
public static Expression<Func<FavouriteItem, bool>> IsVisibleExpression<FavouriteItem>()
{
return favourite => favourite.Item.DateVisible != null &&
favourite.Item.DateVisible <= DateTime.Now;
}
But this again just duplicates the code. Is there any way the same code be used between them. I know I could do a join on the itemRepository.QueryAll().WhereVisible(), but is there not a way to do this that doesn’t involve doing this everywhere?
Update: I tried creating an interface for the classes:
public interface IEntityWithItem
{
Item Item { get; set; }
}
And creating an expression for it:
public static IQueryable<TEntity> WhereItemVisible<TEntity>(this IQueryable<TEntity> queryable) where TEntity : IEntityWithItem
{
return queryable.Where(ui => ui.Item.DateVisibleFrom != null &&
ui.Item.DateVisibleFrom.Value <= DateTime.Now);
}
Which I’ve called from:
// Get the favourites
var favourites = favouriteItemRepository.QueryAll()
.WhereItemVisible()
.Where(favourite => favourite.UserId == user.Id);
But it gives the error: “Unable to cast the type ‘FavouriteItem’ to type ‘IEntityWithItem’. LINQ to Entities only supports casting Entity Data Model primitive types.“
There is to my knowledge no easy way to achieve this, however have a look at PredicateBuilder, which does solve some of these issues: http://www.albahari.com/nutshell/predicatebuilder.aspx.
The basic idea is that you define interfaces with the common properties, which allows you to define generic expressions constrained to that interface.
Start by definining these interfaces:
This will allow you to write the query expressions in a generic way, obtaining at least some reuse:
You will still not be able to easily achieve reuse between expressions like
x.DateVisibleandx.Item.DateVisiblehowever.UPDATE: it turns out that ‘unnecessary’ cast operations are being added in the generated expression tree, which are then not supported by the entity framework provider when translating to SQL.
You can apply a similar trick as the one in this question to remove these unnecessary cast operations ‘manually’. Becomes a bit hairy though IMHO…