EF / LINQ: где () для свойства подтипа - PullRequest
0 голосов
/ 03 июня 2010

У меня есть набор POCO, каждый из которых реализует следующий простой интерфейс:

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

Подмножество этих POCO реализует этот дополнительный интерфейс:

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

У меня есть иерархия хранилища, которая выглядит примерно так:

IRepository <: BasicRepository <T><: ValidatingRepository <T>(где T - это IIdObject)

Я пытаюсь добавить FilteringRepository в иерархию таким образом, чтобы у всех POCO, которые реализуют IDeletableObject, был применен фильтр 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 поддерживает только приведение типов примитивов Entity Data Model."

Я продолжал пробовать некоторые причудливые параметризованные решения, но в итоге они потерпели неудачу, потому что я не мог сделать T-ковариантным в следующем случае по какой-то причине, которую я не совсем понимаю:

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

Я был бы рад более подробно рассказать о моих более сложных решениях, если бы это помогло, но я думаю, что пока остановлюсь здесь в надежде, что у кого-то может появиться идея попробовать.

Заранее большое спасибо!

1 Ответ

1 голос
/ 04 июня 2010

Это слишком большой комментарий, поэтому ...

Вы можете создавать выражения динамически. Я создал вспомогательные методы:

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

EDIT

Вы также можете использовать Динамическая библиотека Linq . Он также использует выражения, но не заставляет задумываться о том, как все это работает, просто записывает простые условия в виде строки Я не знаю, как он обрабатывает значения bool.

...