0

je la méthode suivante dans une classe d'accès aux données qui utilise Entity Framework:Passer une requête GetWhere (Func <entityDTO, bool>) à une méthode de couche de données qui a besoin d'un (Func <entity,bool>) paramètre à travailler

public static IEnumerable<entityType> GetWhere(Func<entityType, bool> wherePredicate) 
{ 
    using (DataEntities db = new DataEntities()) 
    { 
     var query = (wherePredicate != null) 
      ? db.Set<entityType>().Where(wherePredicate).ToList() 
      : db.Set<entityType>().ToList();      
     return query; 
    } 
} 

Cela fonctionne bien lorsque j'utilise les entités à travers toutes les couches ... mais je suis en train de passer à l'aide d'une classe DTO et je voudrais faire quelque chose comme ce qui suit:

public static IEnumerable<EntityTypeDTO> GetWhere(Func<EntityTypeDTO, bool> wherePredicate) 
{ 
    //call a method here which will convert Func<EntityTypeDTO,bool> to 
    // Func<EntityType,bool> 

    using (DataEntities db = new DataEntities()) 
    { 
     var query = new List<EntityType>(); 
     if (wherePredicate == null) 
     { 
      query = db.Set<EntityType>().ToList(); 
     } 
     else 
     { 
      query = (wherePredicate != null) 
       ? db.Set<EntityType>().Where(wherePredicate).AsQueryable<EntityType>().ToList() 
       : db.Set<EntityType>().ToList(); 
     } 
     List<EntityTypeDTO> result = new List<EntityTypeDTO>(); 
     foreach(EntityType item in query) 
     { 
      result.Add(item.ToDTO()); 
     } 

     return result; 
    } 
} 

Essentiellement, je veux une méthode qui va convertir Func en Func.

Je pense que je dois décomposer le Func dans un arbre d'expression, puis le reconstruire d'une manière ou d'une autre dans entityType?

Je veux faire ceci pour permettre au calque de présentation de passer juste les requêtes d'expression? Ai-je manqué quelque chose de basique ou existe-t-il un modèle de conception plus facile qui peut transmettre une requête d'un DTO à une classe d'accès aux données sans connaître les détails de la requête?

J'ai essayé de faire en sorte que le DTO hérite de l'entité qui ne semble pas fonctionner non plus?

S'il y a un meilleur modèle de conception que je suis absent, j'aimerais un pointeur et je peux étudier à partir de là ...

Répondre

2

Tout d'abord, je vous suggère de mettre une couche d'interrogation de votre propre devant l'entité Framework plutôt que de permettre la transmission de n'importe quel Func arbitraire car il sera très facile à l'avenir de passer un Func que Entity Framework ne peut pas traduire en une instruction SQL (il peut seulement traduire certaines expressions - les bases sont bien mais si votre expression appelle une méthode C#, par exemple, Entity Framework échouera probablement). Ainsi, votre couche de recherche peut avoir des classes que vous créez en tant que critères (par exemple une classe de recherche "ContainsName" ou une classe "ProductHasId") qui sont ensuite traduites en expressions dans votre couche de recherche. Cela sépare votre application entièrement de l'ORM, ce qui signifie que les détails de l'ORM (comme les entités ou les limitations de ce que Funcs peut et ne peut pas être traduit) ne fuient pas. Il y a beaucoup de choses qui ont été écrites à ce sujet.

Une dernière remarque, cependant, si vous sont travailler à proximité de la couche ORM, Entity Framework est très intelligent et vous pourriez probablement obtenir un long chemin sans essayer de traduire votre Func < DTO, bool > à un Func < entité, bool >. Par exemple, dans le code ci-dessous, l'accès à «context.Products» renvoie un «DbSet» et l'appel de Select le renvoie un IQueryable et appelle Où sur ce également renvoie un IQueryable.Entity Framework va traduire tous de cela en une seule instruction SQL de sorte que ne va pas tirer tous les autres produits en mémoire, puis filtrer l'ID sur cet ensemble de mémoire, il va effectivement effectuer le filtrage en SQL même si le filtre est opérant sur un type prévu (ce qui équivaut à la DTO dans votre cas) et non l'entité Entity Framework -

var results = context.Products 
    .Select(p => new { ID = p.ProductID, Name = p.ProductName }) 
    .Where(p => p.ID < 10) 
    .ToList(); 

le SQL exécuté est:

SELECT 
    [Extent1].[ProductID] AS [ProductID], 
    [Extent1].[ProductName] AS [ProductName] 
FROM [dbo].[Products] AS [Extent1] 
WHERE [Extent1].[ProductID] < 10 

donc, si vous avez changé votre code obtenir quelque chose comme ..

return context.Products 
    .Map<Product, ProductDTO()>() 
    .Where(productDtoWherePredicate) 
    .ToList(); 

.. alors vous pourriez être très bien avec les Funcs que vous avez déjà. Je suppose que vous avez déjà des fonctions de mappage pour passer des entités EF aux DTO (mais si ce n'est pas le cas, vous pouvez utiliser AutoMapper pour vous aider - qui supporte les "projections", qui sont essentiellement des cartes IQueryable).

0

Je vais mettre cela comme une réponse. Merci à Dan pour la réponse rapide. En regardant ce que vous dites, je peux écrire un ensemble de requêtes/filtres de classes. par exemple, prenez le code suivant:

GetProducts().GetProductsInCategory().GetProductsWithinPriceRange(minPrice, maxPrice); 

Ce code fonctionner comme ceci: Obtenez les produits obtiendraient tous les produits de la table et les fonctions restantes seraient filtrer les résultats. Si toutes les requêtes s'exécutent comme ceci, il se peut que la charge sur les connexions Data Access Layer/DB Server soit importante.

ou

Un suppléant, je travaillerai sur est aussi: Si chaque fonction crée une expression Linq, je pouvais les combiner comme ceci: How do I combine multiple linq queries into one results set? cela peut me permettre de faire cela d'une manière où je peux retourner les résultats filtrés définis à partir de la base de données.

De toute façon je marque cela comme étant répondu. Je vais mettre à jour quand j'ai plus de détails.

+0

Si GetProducts retourne un IQueryable plutôt que d'un IEnumerable et les deux instances puis Entity Framework GetProductsInCategory et GetProductsWithinPriceRange et renvoient IQueryable sera en mesure de prendre tout ce travail et de le convertir en une seule instruction SQL qui signifie que le filtrage est Tout est fait dans la base de données et pas en mémoire dans votre couche d'accès. Lorsque vous filtrez IEnumerable alors vous le faites en mémoire mais tant que vous travaillez sur IQueryable le travail peut être déchargé à la base de données. –

+0

Pour votre Plan B, il y aura quelques complications dans la combinaison de vos fonctions de critères - vous voulez que chacune soit une Expression plutôt qu'un Func pour qu'EF puisse les traduire en SQL clauses (et évitent d'avoir à faire le filtrage en mémoire, ce qui nécessiterait probablement beaucoup plus de données de la base de données que ce qui est strictement nécessaire). Il y a un exemple de code dans une réponse que j'ai laissée sur une autre question qui devrait aider: http://stackoverflow.com/a/37907316/3813189 –