Linq, Expression et Compiled Queries

Linq, Expression et Compiled Queries - C#/.NET managed - Programmation

Marsh Posté le 30-09-2013 à 14:59:57    

Une petite question pour les gourous du C# :D
 
J'utilise l'entity framework en ORM et des requêtes compilée pour les requêtes DB (Perfs + me permettent de ressortir directement les "types" d'objet dont j'ai besoin).
 
Pour "simplifier" la maintenance du code j'aurais voulu décomposer mes compiled queries en expression et "sous" expression
 
Ex j'ai une entité légale qui possède 3 propriétés, un Id, un Nom et un objet Company.
ma Company à 2 propriétés, un Id, un Nom
 
Classiquement j'ecrirais

Code :
  1. public static Func<MonContext, int, MonTypeEntite> GetEntiteById = CompiledQuery.Compile<MonContext, int, MonTypeEntite>
  2.   ((context, id) =>
  3.     (from entite in context.ENTITE
  4.      where entite.Id == id
  5.      select new MonTypeEntite
  6.      {
  7.         Id = entite.Id,
  8.         Name = entite.Name,
  9.         Company = (from company in context.COMPANY
  10.                             where company.Id == entite.FKCompany
  11.                             select new MonTypeCompany
  12.                             {
  13.                                Id = company.Id,
  14.                                Name = company.Name
  15.                              }).FirstOrDefault()
  16.     }).FirstOrDefault());

 
Et j'appellerais ma compiled query en lui passant les params, on est d'accord que ça fonctionne très bien, par contre avec beaucoup d'objets imbriqués ça devient une vraie purge (700 ligne de requête merci mais sans façon...)
 
donc j'aurais voulu faire
 

Code :
  1. public static Func<MonContext, int, MonTypeCompany> GetCompanyById = CompiledQuery.Compile<MonContext, int, MonTypeCompany>
  2.   (context, id) =>   
  3.     (from company in context.COMPANY
  4.      where company.Id == entite.FKCompany
  5.      select new MonTypeCompany
  6.      {
  7.         Id = company.Id,
  8.         Name = company.Name
  9.      }).FirstOrDefault());
  10. public static Func<MonContext, int, MonTypeEntite> GetEntiteById = CompiledQuery.Compile<MonContext, int, MonTypeEntite>
  11.   ((context, id) =>
  12.     (from entite in context.ENTITE
  13.      where entite.Id == id
  14.      select new MonTypeEntite
  15.      {
  16.         Id = entite.Id,
  17.         Name = entite.Name,
  18.         Company = GetCompanyById(context, entite.FKCompany)
  19.      }).FirstOrDefault());


 
mais malheureusement ça n'a pas l'air supporté par EF... J'ai aussi essayé de remplacer les compiled queries par des System.Linq.Expressions.Expression mais ça ne fonctionne pas mieux.
 
Apparemment je ne peux pas générer de CompiledQuery de façon dynamique, une idée pour contourner le problème?
 
Merci


Message édité par snipeangel le 30-09-2013 à 15:17:36

---------------
http://forum.hardware.fr/hfr/Achat [...] 0043_1.htm
Reply

Marsh Posté le 30-09-2013 à 14:59:57   

Reply

Marsh Posté le 30-09-2013 à 16:48:32    

Avec des jointures plutôt que des sous requetes...
 
En plus d'être moins lourd à coder, ça sera beaucoup² plus perfomant :
 

Code :
  1. from entite in context.ENTITE
  2.      where entite.Id == id
  3.      join company in context.COMPANY on entite.FKCompany equals company.Id
  4.    
  5.      select new MonTypeEntite
  6.      {
  7.          Id = entite.Id,
  8.          Name = entite.Name,
  9.          Company = company
  10.      }


---------------
VA APPRENDRE ET REVIENS QUAND TU SAIS, SINON ABSTIENT TOI C'EST UN GRAND CONSEIL QUE JE TE DONNE... TU ES INCOMPÉTENT ET C'EST UNE RÉALITÉ, TU N'AS RIEN A FAIRE ICI FAUT S'Y CONNAITRE ... -Jojo1998 - RIP - http://tinyurl.com/qc47ftk
Reply

Marsh Posté le 30-09-2013 à 16:58:05    

Mieux encore, car j'ai l'impression que tu cherchais à réaliser une Left outer join, la version exacte est :
 

Code :
  1. from entite in context.ENTITE
  2.      where entite.Id == id
  3.      join company in context.COMPANY on entite.FKCompany equals company.Id into joinedCompagny
  4.    from company in joinedCompagny.DefaultIfEmpty()
  5.      select new MonTypeEntite
  6.      {
  7.          Id = entite.Id,
  8.          Name = entite.Name,
  9.          Company = company
  10.      }


---------------
VA APPRENDRE ET REVIENS QUAND TU SAIS, SINON ABSTIENT TOI C'EST UN GRAND CONSEIL QUE JE TE DONNE... TU ES INCOMPÉTENT ET C'EST UNE RÉALITÉ, TU N'AS RIEN A FAIRE ICI FAUT S'Y CONNAITRE ... -Jojo1998 - RIP - http://tinyurl.com/qc47ftk
Reply

Marsh Posté le 30-09-2013 à 17:21:41    

Merci mon cher ixemul :O
Mais ce que tu me proposes la est un brin trop basique et c'est ce que je faisais jusqu'à maintenant :jap:
 
Mon but est d'avoir une CompiledQuery ou une expression qui me permettrait de faire un GetCompanyById tout en réinjectant cette requete dans diverse requete ou j'ai besoin de l'objet company
 
Un exemple un peu plus parlant:
J'ai un objet contrat  
cet objet possède n attributs dont 4 qui sont de meme type mais différent (4 entité légales)
Avec ma vieille méthode je me tapais une requete compilé pour récupérer un entitié légale par Id et dans le get contract j'étais obligé de copier quatre fois cette portion de code.
 
Pas propre pour la maintenance...
 
Le but est d'avoir une expression que je puisse  réinjecter à la construction de la compiled query comme ça si l'objet est modifié je n'ai qu'un point dans toute l'application et non pas 20 dont certains dans des compiled queries de 700 lignes :/


---------------
http://forum.hardware.fr/hfr/Achat [...] 0043_1.htm
Reply

Marsh Posté le 01-10-2013 à 10:03:08    

Ok, je comprends mieux ce que tu veux faire.
 
Je decele un petit soucis dans ton 2eme exemple,
 
ublic static Func<MonContext, int, MonTypeCompany> GetCompanyById = CompiledQuery.Compile<MonContext, int, MonTypeCompany>
  (context, id) =>    
    (from company in context.COMPANY
     where company.Id == entite.FKCompany
     select new MonTypeCompany
     {
        Id = company.Id,
        Name = company.Name
     }).FirstOrDefault());
Là, en rouge, ce devrait être id et non entite.FKCompany.
 
ça ne marche pas mieux ? dsl, je ne peux pas tester, ici je bosse avec Sybase et nous n'avons pas de connecteur pour entity Framework...
 
Sinon, en l'absence de solution, tu peux te tourner vers de la génération de code via T4 (langage de template MS intégré à visual studio). Cela te permettrai de maintenir uniquement le script de génération et laisserai la génération de la partie lourde du code au templating.


---------------
VA APPRENDRE ET REVIENS QUAND TU SAIS, SINON ABSTIENT TOI C'EST UN GRAND CONSEIL QUE JE TE DONNE... TU ES INCOMPÉTENT ET C'EST UNE RÉALITÉ, TU N'AS RIEN A FAIRE ICI FAUT S'Y CONNAITRE ... -Jojo1998 - RIP - http://tinyurl.com/qc47ftk
Reply

Marsh Posté le 01-10-2013 à 11:47:26    

oui pardon c'etait bien Id, mauvais CC :D
 
Je ne pense pas qu'il soit possible de générer des RC via template mon but était plus d'injecter des portions de code (Expression ou appel de Func) dans Linq to SQL, il semble que ce ne soit pas possible en l'état.
 
Je vais regarder du côté de ExpressionVisitor


---------------
http://forum.hardware.fr/hfr/Achat [...] 0043_1.htm
Reply

Marsh Posté le 01-10-2013 à 11:51:42    

snipeangel a écrit :

oui pardon c'etait bien Id, mauvais CC :D
 
Je ne pense pas qu'il soit possible de générer des RC via template mon but était plus d'injecter des portions de code (Expression ou appel de Func) dans Linq to SQL, il semble que ce ne soit pas possible en l'état.
 
Je vais regarder du côté de ExpressionVisitor


 
Le soucis que tu mets en évidence, c'est que tu parts sur des requête compilée, c.à.d, du SQL qui sera généré à partir de ta requête LINQ, qui elle même sera compilée.
 
En faisant ce que tu propose, je vois mal comment l'injection d'une requête précompilée (Select Company) sera injectée dans une nouvelle requête (Select entite).
 
Il est possible de générer tes requetes par Template, le code sera simplement généré (t'épargnant la saisie de tes 700 tables), et simplement compilé avec le reste :jap:


---------------
VA APPRENDRE ET REVIENS QUAND TU SAIS, SINON ABSTIENT TOI C'EST UN GRAND CONSEIL QUE JE TE DONNE... TU ES INCOMPÉTENT ET C'EST UNE RÉALITÉ, TU N'AS RIEN A FAIRE ICI FAUT S'Y CONNAITRE ... -Jojo1998 - RIP - http://tinyurl.com/qc47ftk
Reply

Marsh Posté le 01-10-2013 à 12:01:16    

Un autre exemple qui devrait de permettre de mieux comprendre ce que je cherche à faire
 

Code :
  1. public static class LocationRequests
  2.     {
  3.         public static class BO
  4.         {
  5.             public static Expression<Func<TestCompiledContainer, int, BOLocationInfos>> ExpressionGetLocationInfos =
  6.                 (context, id) =>
  7.                     (from location in context.DM_LOCATION
  8.                      where location.IdLocation == id
  9.                      select new BOLocationInfos
  10.                      {
  11.                          IdLocationInfosBO = location.IdLocation,
  12.                          CityLocationInfosBO = location.City,
  13.                          TypeInfosBO = TypeRequests.BO.GetTypeInfos(context, location.FKType),
  14.                      }).FirstOrDefault();
  15.             public static Func<TestCompiledContainer, int, BOLocationInfos> GetLocationInfos = CompiledQuery.Compile<TestCompiledContainer, int, BOLocationInfos>(ExpressionGetLocationInfos);
  16.         }
  17.     }


 
la classe BOLocationInfos contenant un objet type
 

Code :
  1. public static class TypeRequests
  2.     {
  3.         public static class BO
  4.         {
  5.             public static Expression<Func<TestCompiledContainer, int, BOTypeInfos>> ExpressionGetTypeInfos =
  6.                 (context, id) =>
  7.                     (from type in context.DM_TYPE
  8.                      where type.IdType == id
  9.                      select new BOTypeInfos
  10.                      {
  11.                          IdTypeInfosBO = type.IdType,
  12.                          LabelTypeBO = type.LabelType
  13.                      }).FirstOrDefault();
  14.             public static Func<TestCompiledContainer, int, BOTypeInfos> GetTypeInfos = CompiledQuery.Compile<TestCompiledContainer, int, BOTypeInfos>(ExpressionGetTypeInfos);
  15.         }
  16.     }


 
et comment je récupère l'objet  
 

Code :
  1. TestCompiledContainer context = new TestCompiledContainer();
  2. BOLocationInfos location = LocationRequests.BO.GetLocationInfos(context, 1);


 
l'exception catchée "Le type de nœud « Invoke » de l'expression LINQ n'est pas pris en charge dans LINQ to Entities."
 
C'est clairement une opération non supportée par Linq et je cherche à contourner ce problème
 
Au temps/autant pour moi :D
 
Tu as répondu pendant que je CC du code.
Il doit etre possible de faire reconnaitre à Linq qu'il doit utiliser une autre portion de code maintenant le tout c'est de trouver comment :D quitte à forcer la précompilation des requêtes pour qu'il puisse utiliser le code SQL généré
De toute faaçon les requêtes sont statiques.


Message édité par snipeangel le 01-10-2013 à 12:04:25

---------------
http://forum.hardware.fr/hfr/Achat [...] 0043_1.htm
Reply

Marsh Posté le 01-10-2013 à 14:04:13    

Une autre approche serait en génération de code au runtime, perso, j'adore le faire, mais :
 
1- C'est très dangereux si mal maitrisé
2- C'est assez chaud pour les non initiés.
 
Le but étant de générer pendant le runtime tes méthodes d'accès au données en injectant le code compilé des méthode d'accès de base  
 
Sinon, tu la joue facile avec LinqKit qui semble permettre de faire ce que tu veux faire ;) :
 
http://www.albahari.com/nutshell/linqkit.aspx


---------------
VA APPRENDRE ET REVIENS QUAND TU SAIS, SINON ABSTIENT TOI C'EST UN GRAND CONSEIL QUE JE TE DONNE... TU ES INCOMPÉTENT ET C'EST UNE RÉALITÉ, TU N'AS RIEN A FAIRE ICI FAUT S'Y CONNAITRE ... -Jojo1998 - RIP - http://tinyurl.com/qc47ftk
Reply

Marsh Posté le 01-10-2013 à 14:23:29    

Ta solution me plait bien :O va falloir que je regarde comment faire ça :D
 
pour LinqKit j' y ai jeté un œil "rapidement" hier et il semble que ça se limite aux "predicate" or ce qui m’intéresse c'est l'attribution de propriétés et non les clauses de recherche qui elles sont fixes (tout du moins pour le moment :whistle:) peut être que d'ici quelques temps je verrais un intérêt à aussi séparer les predicate :jap: .
 


---------------
http://forum.hardware.fr/hfr/Achat [...] 0043_1.htm
Reply

Marsh Posté le 01-10-2013 à 14:23:29   

Reply

Marsh Posté le 26-03-2014 à 15:34:22    

Bon sinon au cas ou pour ceux qui auraient le même soucis, une petite modif de linqkit permet de faire tout ça très bien.
 
On peu du coup "injecter" des expressions dans des expressions sur autant de niveaux qu'on le désire :D


---------------
http://forum.hardware.fr/hfr/Achat [...] 0043_1.htm
Reply

Marsh Posté le 26-03-2014 à 15:37:06    

ha bha voila ! :D


---------------
VA APPRENDRE ET REVIENS QUAND TU SAIS, SINON ABSTIENT TOI C'EST UN GRAND CONSEIL QUE JE TE DONNE... TU ES INCOMPÉTENT ET C'EST UNE RÉALITÉ, TU N'AS RIEN A FAIRE ICI FAUT S'Y CONNAITRE ... -Jojo1998 - RIP - http://tinyurl.com/qc47ftk
Reply

Marsh Posté le 06-05-2014 à 13:54:01    

snipeangel a écrit :

Bon sinon au cas ou pour ceux qui auraient le même soucis, une petite modif de linqkit permet de faire tout ça très bien.
 
On peu du coup "injecter" des expressions dans des expressions sur autant de niveaux qu'on le désire :D


 
 
Dis-nous en plus ...  :whistle:  


---------------
O'.  [ACH] surcapotte Dyane     Nouilles de Yan : CE vendredi 17 mai 2024 @ 19h
Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed