IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

[C#] Linq to Entities, expression lambda et fonction => impossible?


Sujet :

C#

  1. #1
    Membre éclairé
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    284
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 284
    Par défaut [C#] Linq to Entities, expression lambda et fonction => impossible?
    Bonjour à tous,

    Je suis débutant en C# et notamment tout ce qui touche à Linq etc...
    J'ai une classe qui va chercher les données dans ma base de données, les transforme en entité, et me les retourne.

    Prenons un exemple:
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Appli.Test.EL;
     
    namespace Appli.Test.DAL
    {
        public class UserManager
        {
            private MyEntities Context;
     
            public UserManager()
            {
                Context = new MyEntities();
            }
     
            public UserEntity Get(long id)
            {
                return Context.user
                    .Where(u => u.id_user.Equals(id))
                    .Select(u => new UserEntity
                    {
                        Id = u.id_user,
                        Lastname = u.lastname,
                        Firstname = u.firstname
                    })
                    .FirstOrDefault();
            }
     
            public List<UserEntity> GetAll()
            {
                return Context.user
                    .Select(u => new UserEntity
                                     {
                                         Id = u.id_user,
                                         Lastname = u.lastname,
                                         Firstname = u.firstname
                                     })
                    .ToList();
            }
        }
    }
    => ça fonctionne bien

    On peut voir qu'entre Get et GetAll, j'ai une partie commune qui est la création de mon entité. Je pensais pouvoir faire une fonction, histoire de factoriser un peu mon code, et dans des cas un peu plus complexe, ne pas oublié une propriété d'une méthode à l'autre.

    J'ai donc essayé ceci:

    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    public UserEntity Get(long id)
    {
        return Context.user
            .Where(u => u.id_user.Equals(id))
            .Select(u => ToEntity(u))
            .FirstOrDefault();
    }
     
    public List<UserEntity> GetAll()
    {
        return Context.user
            .Select(u => ToEntity(u))
            .ToList();
    }
     
    private static UserEntity ToEntity(user user)
    {
        return new UserEntity
                    {
                        Id = user.id_user,
                        Lastname = user.lastname,
                        Firstname = user.firstname
                    };
    }
    => ça ne fonctionne plus

    J'ai aussi essayé de le faire en passant le paramètre par référence (avec ref devant) mais sans succès! Je ne comprends pas pourquoi ça ne marche pas? Où ai je fais une erreur s'il vous plait?

    EDIT - IntelliTrace:
    LINQ to Entities ne reconnait pas la méthode "Appli.Test.EL.UserEntity ToEntity(Appli.Test.DAL.user) et cette dernière ne peut pas être traduite en expression de magasin.....

  2. #2
    Membre Expert
    Avatar de GuruuMeditation
    Homme Profil pro
    .Net Architect
    Inscrit en
    Octobre 2010
    Messages
    1 705
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Belgique

    Informations professionnelles :
    Activité : .Net Architect
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2010
    Messages : 1 705
    Par défaut
    Linq To Entities n'accepte qu'une partie des operateurs LINQ, et pas de fonctions (en gros). Car la requete doit être traduite en SQL, ce qui n'est pas toujours possible avec certains operateurs ou des fonctions utilisateur

    EDIT:
    voir la page :
    http://msdn.microsoft.com/en-us/library/dd456828.aspx
    Il est expliqué quelles fonctions on peut utiliser en LinQ To Entities, et comment (sous certains conditions) creer des nouvelles.

  3. #3
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Par défaut
    Quel est l'interet de créer un UserEntity? C'est Linq To Entities qui s'en charge normalement...

  4. #4
    Membre éclairé
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    284
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 284
    Par défaut
    @EquinoxeDotNet
    Merci, je vais aller y jeter un coup d'oeil.

    @Nathanael Marchand
    J'ai une Archi n-tiers(GUI/BLL/DAL) Et seul DAL connait les entités de Linq to Entities. C'est pour ça que j'ai en plus une couche entity qui permet de faire navigueur mais objet entre toutes les couches.

    => Mais peut être qu'il ne faudrait pas faire comme ça, je débute donc je n'ai pas forcément les bonnes méthodes.

  5. #5
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Par défaut
    Citation Envoyé par takinelinfo Voir le message
    @EquinoxeDotNet
    Merci, je vais aller y jeter un coup d'oeil.

    @Nathanael Marchand
    J'ai une Archi n-tiers(GUI/BLL/DAL) Et seul DAL connait les entités de Linq to Entities. C'est pour ça que j'ai en plus une couche entity qui permet de faire navigueur mais objet entre toutes les couches.

    => Mais peut être qu'il ne faudrait pas faire comme ça, je débute donc je n'ai pas forcément les bonnes méthodes.
    Généralement ce que je fais dans ces cas la, c'est que mes entités Linq To Entities (qui sont des POCO) sont partagés par toutes les couches. C'est une couche transversale.
    Après ce que tu fais est tout à fait valable mais plus complexe à mettre en place.
    En tout cas, ca n'empeche qu'il faut bien comprendre comment marche linq to entities et tout se joue entre IQueryable<T> et IEnumerable<T>.
    Explications:
    Lorsque tu utilises Context.user, tu peux remarquer que cette collection implémente à la fois IQueryable<T> et IEnumerable<T>. De plus tu remarques que lorsque tu prends un opérateur comme Select(), le paramètre qu'il prend est une expression.
    Tant que tu trimballes des Select, Where, etc sur un IQueryable, cela veut dire que cela va être traduit en SQL derrière par le provider. (Le provider Linq To Entities va traduire les Expressions en SQL, c'est d'ailleurs pour ca que ce sont des Expression<Func<T>> et non des Func<T>.)

    Au bout d'un moment, lorsque tu fais un ToList() ou un AsEnumerable(). La requete est déclenchée, les données sont récupérées et le mappage est effectué. Ce qui signifie qu'à ce moment la, tu manipules des objets et plus des lignes SQL. Lorsque tu fais ton Select, il essaye de le traduire en SQL et n'y arrive pas. Il faudrait que juste avant tu fasses un AsEnumerable().
    Par exemple, pour le GetAll():
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Context.user.AsEnumerable() //declenche la requete SQL
         .Select(u => new UserEntity
              {
                   Id = u.id_user,
                   Lastname = u.lastname,
                   Firstname = u.firstname
              })
         .ToList();
    Pour le Get(id) attention! Tu es tombé dans un piège de débutant avec Linq To SQL (ou Entities). Car si on applique la même solution:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    return Context.user
        .Where(u => u.id_user.Equals(id))
        .AsEnumerable()
        .Select(u => new UserEntity
            {
                Id = u.id_user,
                Lastname = u.lastname,
                Firstname = u.firstname
            })
        .FirstOrDefault();
    Catastrophe! De gros problèmes de performance! Car le FirstOrDefault() est fait en mémoire et donc si Where laisse passer 5000lignes, les 5000lignes seront rapatriés de la base vers la mémoire. C'est seulement une fois mappé que le First intervient.

    Je te suggère ceci plutot:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     var user = Context.user
        .FirstOrDefault(u => u.id_user.Equals(id));
    return new UserEntity
            {
                Id = user.id_user,
                Lastname = user.lastname,
                Firstname = user.firstname
            });

  6. #6
    Membre émérite Avatar de kheironn
    Homme Profil pro
    Chef de projets technique C# / MVC / .Net
    Inscrit en
    Février 2007
    Messages
    822
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Chef de projets technique C# / MVC / .Net
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2007
    Messages : 822
    Par défaut
    Je ne connais pas bien LinQ to entity... je pensais que c'était des DTO, pas des POCO... je vois donc de ce pas me renseigner, je suis bien content d'avoir lu la dernière réponse de Nathanael

    si tu as des bons sites de docs, n'hésite pas à me mp !

  7. #7
    Rédacteur
    Avatar de Nathanael Marchand
    Homme Profil pro
    Expert .Net So@t
    Inscrit en
    Octobre 2008
    Messages
    3 615
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Expert .Net So@t
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2008
    Messages : 3 615
    Par défaut
    Ah mais par défaut ca ne sont pas des POCO, c'est juste que je n'utilise que des POCO personellement quand je travaille avec Linq-To-Entity.
    Mais ca marche aussi avec des entités normales de Linq to Entity.

  8. #8
    Membre éclairé
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    284
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 284
    Par défaut
    Citation Envoyé par Nathanael Marchand Voir le message
    Généralement ce que je fais dans ces cas la, c'est que mes entités Linq To Entities (qui sont des POCO) sont partagés par toutes les couches. C'est une couche transversale.
    Après ce que tu fais est tout à fait valable mais plus complexe à mettre en place.
    En tout cas, ca n'empeche qu'il faut bien comprendre comment marche linq to entities et tout se joue entre IQueryable<T> et IEnumerable<T>.
    Explications:
    Lorsque tu utilises Context.user, tu peux remarquer que cette collection implémente à la fois IQueryable<T> et IEnumerable<T>. De plus tu remarques que lorsque tu prends un opérateur comme Select(), le paramètre qu'il prend est une expression.
    Tant que tu trimballes des Select, Where, etc sur un IQueryable, cela veut dire que cela va être traduit en SQL derrière par le provider. (Le provider Linq To Entities va traduire les Expressions en SQL, c'est d'ailleurs pour ca que ce sont des Expression<Func<T>> et non des Func<T>.)

    Au bout d'un moment, lorsque tu fais un ToList() ou un AsEnumerable(). La requete est déclenchée, les données sont récupérées et le mappage est effectué. Ce qui signifie qu'à ce moment la, tu manipules des objets et plus des lignes SQL. Lorsque tu fais ton Select, il essaye de le traduire en SQL et n'y arrive pas. Il faudrait que juste avant tu fasses un AsEnumerable().
    Par exemple, pour le GetAll():
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Context.user.AsEnumerable() //declenche la requete SQL
         .Select(u => new UserEntity
              {
                   Id = u.id_user,
                   Lastname = u.lastname,
                   Firstname = u.firstname
              })
         .ToList();
    Pour le Get(id) attention! Tu es tombé dans un piège de débutant avec Linq To SQL (ou Entities). Car si on applique la même solution:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    return Context.user
        .Where(u => u.id_user.Equals(id))
        .AsEnumerable()
        .Select(u => new UserEntity
            {
                Id = u.id_user,
                Lastname = u.lastname,
                Firstname = u.firstname
            })
        .FirstOrDefault();
    Catastrophe! De gros problèmes de performance! Car le FirstOrDefault() est fait en mémoire et donc si Where laisse passer 5000lignes, les 5000lignes seront rapatriés de la base vers la mémoire. C'est seulement une fois mappé que le First intervient.

    Je te suggère ceci plutot:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     var user = Context.user
        .FirstOrDefault(u => u.id_user.Equals(id));
    return new UserEntity
            {
                Id = user.id_user,
                Lastname = user.lastname,
                Firstname = user.firstname
            });
    Oulala très bonne explication.
    Je vais tester tout ça demain. J'ai deux petites questions qui me traversent du coup la tête.

    1) Y a t'il un moyen d'afficher ou d'apercevoir les requêtes que fait Linq to Entities? J'ai trouvé ceci : ((ObjectQuery)query).ToTraceString()

    2) Je ne connais pas le fonctionnement interne de Linq to Entities, ai je une méthode facile pour ne pas commettre des erreurs de performances du style:
    Code c# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    return Context.user
        .Where(u => u.id_user.Equals(id))
        .AsEnumerable()
        .Select(u => new UserEntity
            {
                Id = u.id_user,
                Lastname = u.lastname,
                Firstname = u.firstname
            })
        .FirstOrDefault();

  9. #9
    Membre Expert
    Avatar de GuruuMeditation
    Homme Profil pro
    .Net Architect
    Inscrit en
    Octobre 2010
    Messages
    1 705
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : Belgique

    Informations professionnelles :
    Activité : .Net Architect
    Secteur : Conseil

    Informations forums :
    Inscription : Octobre 2010
    Messages : 1 705
    Par défaut
    Un outil ambon sens indispensable pour tout developer LINQ (n'importe quel LINQ) est Linqpad. il permet de voir le sql généré (dans le cas de LINQ 2 EF), et des tas d'autres choses.

Discussions similaires

  1. Réponses: 1
    Dernier message: 26/12/2013, 08h45
  2. Réponses: 4
    Dernier message: 22/03/2011, 14h50
  3. Réponses: 2
    Dernier message: 09/08/2010, 15h41
  4. Linq to Entities disponible dans C# Express ?
    Par rdh123 dans le forum Visual Studio
    Réponses: 1
    Dernier message: 15/06/2008, 12h43
  5. Expression d'une fonction à partir d'une courbe
    Par ramrouma dans le forum MATLAB
    Réponses: 2
    Dernier message: 08/01/2007, 17h52

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo