How to conditionally filter IQueryable by type using generic repository pattern

不问归期 提交于 2019-12-12 15:11:01

问题


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?


回答1:


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;
    }
}


来源:https://stackoverflow.com/questions/20052827/how-to-conditionally-filter-iqueryable-by-type-using-generic-repository-pattern

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!