Search code examples
c#entity-frameworklinqentity-framework-6linq-to-entities

How can I share object level logic across Entity Framework queries?


I am looking to share some very simple logic across multiple queries in Entity Framework. Say I have the following models

public class User
{
    // user properties
    public ICollection<UserRole> Roles { get; set; }
}

public class UserRole : IDateRestricted
{
    public RoleType Type { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime? EndDate { get; set; }
}

public interface IDateRestricted
{
    DateTime StartDate { get; set; }
    DateTime? EndDate { get; set; }
}

If I was using normal LINQ I could create a simple extension method that would allow me to determine if the role was currently active.

public static class DateRestrictedExtensions
{
    public static Boolean IsActive(this IDateRestricted entity)
    {
        return entity.StartDate <= DateTime.Now && (entity.EndDate == null || entity.EndDate > DateTime.Now);
    }
}

and I could use it like so.

var admins = users.Where(x => x.Roles.Any(role => role.Type == RoleType.Admin && role.IsActive()));

With entity framework this fails with the dreaded:

LINQ to Entities does not recognize the method Boolean IsActive(Domain.Interfaces.IDateRestricted) method, and this method cannot be translated into a store expression.

Is there a way that I can share this logic and have LINQ to Entities support it when querying?


Solution

  • You should use Expression<Func<IDateRestricted, bool>> instead of Func<IDateRestricted, bool> - presented exception's description exactly points to it:

    public static class DateRestrictedExtensions
    {
        public static Expression<Func<IDateRestricted, bool>> IsActive()
        {
            return entity => entity.StartDate <= DateTime.Now 
                             && (entity.EndDate == null || entity.EndDate > DateTime.Now);
        }
    }
    
    var admins = users.Where(x => x.Roles.AsQueryable().Where(role.Type == RoleType.Admin)
                                       .Any(DateRestrictedExtensions.IsActive()));