2009-04-20 6 views
7

J'ai certaines requêtes linq qui ont une redondance que je voudrais factoriser un seul morceau de code. Ce sont des expersions de jointure qui sont IQueryable, et son importance je ne provoque pas l'évaluation de la requête plus tôt que ce serait sans le refactoring.refactoring expression LINQ IQueryable pour supprimer des portions dupliquées de requêtes

Voici une requête simplifiée:

var result = 
from T in db.Transactions 
join O in db.Orders on T.OrderID equals O.OrderID 
join OD in db.OrderDetails on O.OrderID equals OD.OrderID into OrderDetails 
let FirstProductBought = OrderDetails.First().Select(OD => OD.Product.ProductName) 
select new 
{ 
    TransactionID = T.TransactionID, 
    OrderID = O.OrderID, 
    FirstProductBought = FirstProductBought 
}; 

Ce que je veux factoriser est e logique « donné un ordre, ce qui est le premier produit acheté ». J'utilise la même logique dans d'autres requêtes. Comment puis-je l'intégrer dans une méthode partagée?

Généralement, pour la réutilisation de code et IQueryables, ce que j'ai pu faire est le code qui prend un IQueryable entrant et produit un IQueryable/IOrderedQueryable en tant que sortie. Avec de telles fonctions, je peux construire des expressions LINQ avec un code réutilisable qui diffère encore la requête jusqu'à ce que la requête soit entièrement construite. Ici, puisque je n'ai qu'un int (le orderID) je ne sais pas comment le faire fonctionner.

grâce

+0

Ceci est une excellente question de haut niveau pour Linq'ers. J'ai demandé la même chose de base et n'ai pas eu beaucoup de commentaires. – Merritt

Répondre

4

Désolé pour répondre à ma propre question, mais j'ai trouvé une bonne solution. Je pense cependant que selon ce que vous essayez de faire, il existe différentes façons de factoriser différentes expressions LINQ sans évaluer le IQueryable. J'espère donc que les gens partagent des solutions alternatives.

Ma solution était de créer une "vue" pour la requête factorisée. Je l'appelle une vue parce qu'elle a beaucoup en commun avec une vue SQL (du point de vue d'un client LINQ). Contrairement à une vue SQL, elle ne peut pas être indexée ou avoir des colonnes persistantes. Donc, en utilisant cette vue devient un goulot d'étranglement, il serait approprié d'utiliser une vue SQL réelle.

static public class MyDataContextExtension 
{ 
    // The view exposes OrderSummary objects 
    public class OrderSummary 
    { 
     public OrderID { get; set; } 
     public string FirstProductListed { get; set; } 
    } 

    static public IQueryable<OrderSummary> OrderySummaryView(this MyDataContext db) 
    { 
     return (
       from O in db.Orders 
       join OD in db.OrderDetails on O.OrderID equals OD.OrderID into OrderDetails 
       let AProductBought = OrderDetails.First().Select(OD => OD.Product.ProductName) 
       let TotalCost = OrderDetails.Aggregate(0 
       select new OrderSummary() 
       { 
        OrderID = OD.OrderID, 
        FirstProductListed = AProductBought.FirstOrDefault() 
       }; 
    } 
} 

Avec cela, je peux tenir la partie dupliquée de la requête, le remplacement de la requête initiale qui suit:

var result = 
from T in db.Transactions 
join OS in db.OrderSummaryView() on T.OrderID equals OS.OrderID 
select new 
{ 
    TransactionID = T.TransactionID, 
    OrderID = T.OrderID, 
    FirstProductBought = OS.FirstProductListed 
}; 

Vous pouvez imaginer d'autres colonnes ajoutées ... Je pense que l'une fraîche En effet, si vous ajoutez des colonnes supplémentaires mais que vous ne les utilisez pas dans votre sélection finale, LINQ n'exécutera pas réellement ces éléments à partir de la base de données.

1

Nous avons eu le même problème. Il n'est pas pris en charge immédiatement et constitue un problème majeur pour les applications métier. J'ai fini par écrire un article de code-projet sur la réutilisation des expressions LINQ, y compris un très petit utilitaire appelé LinqExpressionPrjection qui permet la réutilisation dans les projections (y compris dans les types anonymes).

Voir l'article here.

Vous pouvez obtenir l'ensemble pour la réutilisation de la projection en tant que nuget package et la source est sur CodePlex.

Un certain temps s'est écoulé depuis votre publication. J'espère que c'est toujours utile pour vous. Sinon, peut-être que d'autres lisent ce fil.

+0

merci! Vouliez-vous faire un lien vers http://www.codeproject.com/Articles/402594/Black-Art-LINQ-expressions-reuse? Pour moi, le lien de l'article que vous avez donné des charges à une liste de vos articles. –

0

Une autre façon important de tenir compte des expressions LINQ est de transmettre des expressions autour, par exemple:

X GetSomeX(Expression<Func<Y, X>> map) 
{ 
    return SourceOfYs.Select(map); 
} 

J'ai eu l'idée en regardant dans l'article de l'article de Barak - et même si il fait un peu plus sur ce sujet, je pensais que je mentionne cette pièce à nouveau ici. Cela semble être une première chose évidente à signaler directement.

Questions connexes