3
votes

I am trying to find a way to reuse predicates for filtering entities in EF 6.1.3. I've run into a problem filtering related properties using 'Where'.

E.g. if I have this interface IValidFromTo

public interface IValidFromTo
{
   DateTime StartDate { get; set;}
   DateTime EndDate { get; set; }
}

and a function that returns a predicate for Where :

public class Extensions 
{
  public static Expression<Func<T, bool>> Current<T>() 
  where T : IValidFromTo
  {
    var currentDate = DateTime.Now; 
    return x => x.StartDate <= currentDate && x.EndDate >= currentDate;
  } 
}

See http://www.albahari.com/nutshell/predicatebuilder.aspx for background.

When applied directly to DbSet, this method works.

var query = ctx.Items.Where(Extensions.Current<Item>()); // compiles

But how to make it work with a more complex query dealing with navigation properties?

E.g. if I have a DbSet<Person> with a collection of Item:

public class Person
{
  ...
  public virtual ICollection<Item> Items { get; set; }
}

and I want to project it into an object containing the name of the person and just the current Items, I end up with some rather cluttered code:

var relationQuery = ctx.People.Select(x => new 
    { Name = x.Name,
      CurrentItems = x.Items.AsQueryable().Where(Extensions.Current<Item>())
    });

I wonder if it is possible to improve this code, e.g. to be able to write something like

CurrentItems = x.Items.Current() // quasi an extension method on `ICollection<Item>`?

(writing an extension method on ICollection<IValidFromTo> doesn't work, because EFf wouldn't recognize this method and throw an error)

UPDATE

Seems like this is achievable via a Join (supposing that each Person can only have a single valid item):

var isCurrent= x => <<some condition on x>>;
...  
var validItems = ctx.Items.Where(isCurrent);
var peopleWithCurrentItems = from person in ctx.Persons
                                join item in validItems on person.Id equals item.Owner.Id
                                select new { Person = person, Item = item };

If there may be more than one valid Item per Person, then

var grouped = peopleWithValid.GroupBy(x => x.Person);

However, this version of the query will exclude persons with no matching Items.