EF / LINQ:Where()针对子类型的属性

时间:2010-06-03 18:35:27

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

我有一组POCO,所有这些都实现了以下简单的界面:

 interface IIdObject
 {
     int Id { get; set; }
 }

这些POCO的一个子集实现了这个额外的接口:

 interface IDeletableObject : IIdObject
 {
     bool IsDeleted { get; set; }
 }

我有一个类似于这样的存储库层次结构:

  

IRepository< T> &lt ;: BasicRepository< T> &lt ;: ValidatingRepository< T> (其中T是IIdObject)

我正在尝试向层次结构中添加 FilteringRepository ,以便在执行任何其他查询之前,所有实施IDeletableObject的POCO都会应用Where(p => p.IsDeleted == false)过滤器。我的目标是避免仅为IDeletableObjects重复层次结构。

我的第一次尝试看起来像这样:

public override IQueryable<T> Query()
{
    return base.Query().Where(t => ((IDeletableObject)t).IsDeleted == false);
}

这适用于LINQ to Objects,但当我切换到EF后端时,我得到:“LINQ to Entities仅支持转换实体数据模型基元类型。”

我继续尝试一些更高级的参数化解决方案,但他们最终失败了,因为我不能在以下情况下使用T协变因某些原因我不太明白:

interface IQueryFilter<out T>  // error
{
    Expression<Func<T, bool>> GetFilter();
}

我很乐意详细介绍我的更复杂的解决方案,如果它会有所帮助,但我想我会暂时停在这里,希望有人可能有想法让我尝试。

非常感谢!

1 个答案:

答案 0 :(得分:1)

这太大而无法发表评论,所以......

您可以动态创建表达式。我已经创建了辅助方法:

public static class ExpressionHelper
{
    public static MemberExpression PropertyExpression(this Expression expr,string propertyName)
    {           
        var properties = propertyName.Split('.');

        MemberExpression expression = null;

        foreach (var property in properties)
        {
            if (expression == null)
                expression = Expression.Property(expr, property);
            else
                expression = Expression.Property(expression, property);
        }

        return expression;
    }

    public static BinaryExpression EqualExpression<T>(this Expression expr, string propertyName, T value)
    {
        return Expression.Equal(expr.PropertyExpression(propertyName), Expression.Constant(value, typeof(T)));
    }
}

然后你可以使用:

//Checking if T implements IDeletableObject
if (typeof(IDeletableObject).IsAssignableFrom(typeof(T)))
{
    //a
    var parameter = Expression.Parameter(typeof(T), "a");
    //a.IsDeleted == false
    var where = parameter.EqualExpression("IsDeleted", false);
    //a => a.IsDeleted == false
    var condition = Expression.Lambda<Func<T, bool>>(where, parameter);
    list = list.Where(condition);
}

修改

您也可以使用Dynamic Linq Library。它也使用表达式,但不会强迫您考虑它是如何工作的,只需将简单条件写为字符串。我不知道它如何处理bool值。