2010-07-12 3 views
0

À la suite de l'excellente réponse à ma question précédente:Linq Entity Framework - puis-je utiliser une méthode récursive?

Linq Entity Framework generic filter method

J'essaie maintenant de comprendre comment je peux appliquer quelque chose comme récursion à ma solution.

Pour récapituler, au lieu de plusieurs déclarations similaires de cette méthode:

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

} 

J'ai maintenant une seule application qui accepte un type d'interface (IHideable) pour fonctionner sur:

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

} 

Cela fonctionne bien et la solution est propre et compréhensible (grand merci à https://stackoverflow.com/users/263693/stephen-cleary). Ce que j'essaie de faire maintenant est d'appliquer une méthode similaire (ou la même?) À n'importe quelle EntityCollections associée à un EntityObject qui implémente aussi IHideable.

Je fais actuellement l'utilisation de GetActiveEntities() comme ceci:

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 

     } 

    } 

); 

Dans cet exemple, je voudrais avoir les commandes EntityCollection filtrent aussi par GetActiveEntities(), de sorte que la dernière commande retournée ne peut jamais être "caché" un.

Est-il possible de filtrer toutes les EntityCollections implémentant IHideable - peut-être en appliquant un peu de réflexion/récursivité dans GetActiveEntities() et en appelant lui-même? Je dis récursion parce que la meilleure solution irait à plusieurs niveaux en marchant profondément à travers le graphe d'entité.

Ce truc étire mon cerveau!

MISE À JOUR # 1 (déplacé mes commentaires jusqu'ici)

Merci Steve.

Faire accepter la méthode IQuerable comme suggéré donne cette erreur:

'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?) 

Je suppose que c'est parce que EntityCollection ne met pas en œuvre IQueryable.

J'ai été capable d'aller plus loin en créant une seconde méthode d'extension qui acceptait explicitement EntityCollection et renvoyait IEnumerable. Ce compilable mais lors de l'exécution a donné cette erreur:

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. 

J'ai aussi essayé d'appeler AsQueryable() sur le EntityCollection et le retour IQueryable, mais la même erreur est revenue.

Répondre

2

Edité à utiliser ObjectSet au lieu de EntityCollection

Je recommande d'utiliser ma solution, mais comme une méthode d'extension, qui peut être appliquée à toute requête:

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

Cela peut ensuite être utilisé comme tel:

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

Merci pour la réponse Stephen. J'ai refactored la méthode dans une méthode d'extension plus tôt aujourd'hui, mais je ne voulais pas embourber cet exemple, donc utilisé un appel de méthode. Bon à savoir, c'était le bon choix - une grande partie de la fonctionnalité du langage C# est nouvelle pour moi. – FantasticJamieBurns

+0

Merci Steve, j'ai déplacé mes commentaires à la zone de question pour une lecture plus facile ... – FantasticJamieBurns

+0

Vous avez raison: 'EntityCollection' n'implémente pas' IQueryable'. J'ai mis à jour ma réponse pour utiliser 'Entities.Orders' plutôt que' product.Orders', ce qui devrait fonctionner. (J'ai aussi changé le calcul 'latestOrder' pour utiliser' orderby'). –

Questions connexes