Linq Entity Framework можно ли использовать рекурсивный метод?

В продолжение отличного ответа на мой предыдущий вопрос:

Универсальный метод фильтрации Linq Entity Framework

Сейчас я пытаюсь понять, как я могу применить что-то вроде рекурсии к моему решению.

Напомним, вместо нескольких похожих объявлений этого метода:

protected IQueryable<Database.Product> GetActiveProducts( ObjectSet<Database.Product> products ) {

    var allowedStates = new string[] { "Active" , "Pending" };

    return (
        from product in products
        where allowedStates.Contains( product.State )
            && product.Hidden == "No"
        select product
    );

}

Теперь у меня есть единственная реализация, которая принимает тип интерфейса (IHideable) для работы:

protected IQueryable<TEntity> GetActiveEntities<TEntity>( ObjectSet<TEntity> entities ) where TEntity : class , Database.IHideable {

    var allowedStates = new string[] { "Active" , "Pending" };

    return (
        from entity in entities
        where allowedStates.Contains( entity.State )
            && entity.Hidden == "No"
        select entity
    );

}

Это работает хорошо, и решение чистое и понятное (большое спасибо https://stackoverflow.com/users/263693/stephen-cleary ).

Что я пытаюсь сделать сейчас, так это применить аналогичный (или тот же?) метод к любым коллекциям EntityCollections, связанным с EntityObject, которые также реализуют IHideable.

В настоящее время я использую GetActiveEntities() следующим образом:

var products = GetActiveEntities( Entities.Products );

return (

    from product in products

    let latestOrder = product.Orders.FirstOrDefault( 
        candidateOrder => (
            candidateOrder.Date == product.Orders.Max( maxOrder => maxOrder.Date )
        )       
    )

    select new Product() {

        Id = product.Id ,
        Name = product.Name,
        LatestOrder = new Order() {

            Id = latestOrder.Id ,
            Amount = latestOrder.Amount,
            Date = latestOrder.Date

        }

    }

);

В этом примере я хотел бы, чтобы коллекция Orders EntityCollection также фильтровалась с помощью GetActiveEntities(), чтобы последний возвращаемый заказ никогда не был «скрытым».

Возможно ли, чтобы все EntityCollections, реализующие IHideable, были отфильтрованы - возможно, путем применения некоторого отражения/рекурсии внутри GetActiveEntities() и вызова самого себя? Я говорю о рекурсии, потому что лучшим решением будет многоуровневый обход графа сущностей.

Этот материал растягивает мой мозг!

ОБНОВЛЕНИЕ №1 (мои комментарии перемещены сюда)

Спасибо, Стив.

Если метод принимает IQuerable, как было предложено, возникает эта ошибка:

'System.Data.Objects.DataClasses.EntityCollection<Database.Order>' does not contain a definition for 'GetActiveEntities' and no extension method 'GetActiveEntities' accepting a first argument of type 'System.Data.Objects.DataClasses.EntityCollection<Database.Order>' could be found (are you missing a using directive or an assembly reference?)

Я предполагаю, что это связано с тем, что EntityCollection не реализует IQueryable.

Я смог продвинуться дальше, создав второй метод расширения, который явно принимал EntityCollection и возвращал IEnumerable. Это скомпилировано, но во время выполнения выдало эту ошибку:

LINQ to Entities does not recognize the method 'System.Collections.Generic.IEnumerable`1[Database.Order] GetActiveEntities[Order](System.Data.Objects.DataClasses.EntityCollection`1[Database.Order])' method, and this method cannot be translated into a store expression.

Я также попытался вызвать AsQueryable() для EntityCollection и вернуть IQueryable, но вернулась та же ошибка.


person FantasticJamieBurns    schedule 12.07.2010    source источник


Ответы (1)


Отредактировано для использования ObjectSet вместо EntityCollection

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

public static IQueryable<TEntity> WhereActive<TEntity>(
    this IQueryable<TEntity> entities)
    where TEntity : class , Database.IHideable
{
    var allowedStates = new string[] { "Active", "Pending" };

    return (
        from entity in entities
        where allowedStates.Contains(entity.State)
            && entity.Hidden == "No"
        select entity
    );
}  

Затем это можно использовать как таковое:

var products = Entities.Products.WhereActive();

return (

    from product in products

    let latestOrder = (
        from order in Entities.Orders.WhereActive()
        where order.ProductId == product.Id
        orderby candidateOrder.Date descending
        select order).FirstOrDefault()

    select new Product()
    {
        Id = product.Id,
        Name = product.Name,
        LatestOrder = new Order()
        {
            Id = latestOrder.Id,
            Amount = latestOrder.Amount,
            Date = latestOrder.Date
        }
    }
);
person Stephen Cleary    schedule 12.07.2010
comment
Спасибо за ответ Стивен. На самом деле сегодня я реорганизовал этот метод в метод расширения, но не хотел увязнуть в этом примере, поэтому использовал вызов метода. Приятно осознавать, что это был правильный шаг — большая часть функциональности языка C# для меня в новинку. - person FantasticJamieBurns; 12.07.2010
comment
Спасибо, Стив, я переместил свои комментарии в область вопросов для облегчения чтения... - person FantasticJamieBurns; 12.07.2010
comment
Вы правы: EntityCollection не реализует IQueryable. Я обновил свой ответ, чтобы использовать Entities.Orders, а не product.Orders, что должно работать. (Я также изменил вычисление latestOrder, чтобы использовать orderby). - person Stephen Cleary; 12.07.2010