如何使用通用存储库模式按类型有条件地过滤IQueryable

时间:2013-11-18 16:31:54

标签: c# entity-framework generics linq-to-entities

我的通用存储库中有一个返回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;
        }
    }

if语句的原因是大多数但不是所有表都有OrganisationID,我想确保用户只能看到他们所属组织的数据。上面的代码有效,但为了使其工作,我必须添加AsEnumerable()以将数据拉入内存。没有它,我收到错误消息

  

“无法将类型'Models.xxx'强制转换为类型   'Models.OrganisationDependent'。 LINQ to Entities仅支持强制转换   EDM原语或枚举类型“

我的所有实体都直接继承ModelBaseOrganisationDependent

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

有没有办法可以克服这个限制,这样当类型是OrganisationDependent的子类时,我会在OrganisationID上过滤而不必将查询拉入内存?

1 个答案:

答案 0 :(得分:4)

最简单的选择可能是使用LINQ Expressions API动态构建过滤器表达式:

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