3

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."

stevehayter
  • 796
  • 1
  • 7
  • 23

1 Answers1

0

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:

public interface IHaveItem
{
   Item Item {get;}
}

public interface IDateVisible
{
   DateTime? DateVisible {get;}
}

This will allow you to write the query expressions in a generic way, obtaining at least some reuse:

public static Expression<Func<T,bool>> IsVisible<T>(DateTime now) 
    where T:IHaveItem
{
   return x => x.Item.DateVisible != null &&
          x.Item.DateVisible < now;
}

You will still not be able to easily achieve reuse between expressions like x.DateVisible and x.Item.DateVisible however.

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...

Community
  • 1
  • 1
jeroenh
  • 26,362
  • 10
  • 73
  • 104
  • Interesting... thanks Jeroenh. I'll take a look at that. I did try adding an IEntityWithItem interface to my classes, and creating an expression that was - but EF was throwing the error 'LINQ to Entities only supports casting Entity Data Model primitive types'. Would the same thing not happen here? – stevehayter Nov 14 '12 at 10:24
  • I've updated the question with what I tried. It onlt differs in that I haven't added an interface for the date property - just the Item. Would this make a difference? – stevehayter Nov 14 '12 at 10:52
  • Thanks @jeroenh. I'll give that a go, but it does seem a very roundabout way of solving the problem. It would seem there is no built in way to solve issues like this, but have accepted your answer as confirmation of this. – stevehayter Nov 15 '12 at 14:06