Colin
Colin

Reputation: 22595

How to conditionally filter IQueryable by type using generic repository pattern

I have a method in my generic repository that returns an IQueryable<T>:

    public virtual IQueryable<T> All
    {
        get
        {
            DbSet<T> set = Context.Set<T>();

            if (typeof(T).IsSubclassOf(typeof(OrganisationDependent)))
                return set.AsEnumerable()
                          .Cast<OrganisationDependent>()
                          .Where(x => x.OrganisationID == CurrentOrganisationID)
                          .AsQueryable()
                          .Cast<T>();

            return set;
        }
    }

The reason for the if statement is that most, but not all of my tables have an OrganisationID and I want to ensure that a user only sees data for the organisation they belong to. The above code works but in order to make it work I had to add the AsEnumerable() to pull the data into memory. Without it I get the error message

"Unable to cast the type 'Models.xxx' to type 'Models.OrganisationDependent'. LINQ to Entities only supports casting EDM primitive or enumeration types"

All my entities directly inherit either ModelBase or OrganisationDependent:

public abstract class OrganisationDependent : ModelBase
{
    public int OrganisationID { get; set; }

    public virtual Organisation Organisation { get; set; }
}

public abstract class ModelBase
{
    public int ID { get; set; }
}

Is there a way that I can overcome this restriction so that when the type is a sub class of OrganisationDependent, I filter on OrganisationID without having to pull the query into memory?

Upvotes: 3

Views: 1925

Answers (1)

Richard Deeming
Richard Deeming

Reputation: 31218

The simplest option is probably to use the LINQ Expressions API to build a filter expression dynamically:

private static readonly PropertyInfo _organizationIdProperty 
    = typeof(OrganisationDependent).GetProperty("OrganisationID");

private static Expression<Func<T, bool>> FilterByOrganization<T>(int organizationId)
{
    var item = Expression.Parameter(typeof(T), "item");
    var propertyValue = Expression.Property(item, _organizationIdProperty);
    var body = Expression.Equal(propertyValue, Expression.Constant(organizationId));
    return Expression.Lambda<Func<T, bool>>(body, item);
}

public virtual IQueryable<T> All
{
    get
    {
        IQueryable<T> set = Context.Set<T>();
        if (typeof(T).IsSubclassOf(typeof(OrganisationDependent)))
        {
            var filter = FilterByOrganization<T>(CurrentOrganisationID);
            set = set.Where(filter);
        }

        return set;
    }
}

Upvotes: 4

Related Questions