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

Entity Framework Discussion :

Test unicité EF5 [Débutant]


Sujet :

Entity Framework

  1. #1
    Membre habitué
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2011
    Messages : 258
    Points : 126
    Points
    126
    Par défaut Test unicité EF5
    Bonjour,
    J'aimerais tester mes enregistrements avant de les envoyer dans SQL Server et éviter les doublons
    Je n'ai pas trouvé de fonction pour cela dans EF5, et j'ai donc repris un code trouvé dans un bouquin en surchargeant la validation pour chaque classe

    Code vb.net : 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
    44
    45
    46
       Protected Overrides Function ValidateEntity(ByVal entityEntry As Entity.Infrastructure.DbEntityEntry, ByVal items As IDictionary(Of Object, Object)) As DbEntityValidationResult
            Dim result = New DbEntityValidationResult(entityEntry, New List(Of DbValidationError)())
            ' les tests par propriété sont réalisés dans la classe
            ' A priori Nullable et Maxlenght sont gérés par par EF
     
            ''test unicité du libellé -> ce test ne doit être effectué que si EntityState.Added, sinon erreur dès modification, le test est fait sur base locale (plusieurs lignes ss enregistrement) et distante
            '' Pb si on teste également les modified  car leur type devient proxy :
            ' exemple : HistoAction -> System.Data.Entity.DynamicProxies.HistoAction_33F4B4452EB9B48D8D7617671B4CD5EBC47CCA7C366ABBECC1DB08FD54CC99C8
     
            If entityEntry.State = EntityState.Added Then
     
                NomClasse = entityEntry.Entity.GetType.Name
     
                Select Case entityEntry.Entity.GetType
     
                    Case GetType(Activite)
                        Dim Act As Activite = TryCast(entityEntry.Entity, Activite)
                        'test unicité du libellé
                        IDDoublon = Act.ActiviteID
                        CritereDoublon = Act.Libelle
                        NbreOccuranceLocal = Activites.Local.Where(Function(l) l.Libelle = Act.Libelle).Count()
     
                        If NbreOccuranceLocal > 1 Then
                            result.ValidationErrors.Add(New Entity.Validation.DbValidationError(MessageErreurDebut, "Doublon : Le libellé doit être unique, Nombre Occurances locales = " & NbreOccuranceLocal))
                        Else
                            NbreOccuranceBase = Activites.Where(Function(l) l.Libelle = Act.Libelle).Count()
                            If NbreOccuranceBase > 0 Then
                                result.ValidationErrors.Add(New Entity.Validation.DbValidationError(MessageErreurDebut, "Doublon : Le libellé doit être unique, Nombre Occurances dans base = " & NbreOccuranceBase + 1))
                            End If
                        End If
     
    ' et ainsi de suite pour chaque classe à tester
     
                    Case Else
                        result.ValidationErrors.Add(New Entity.Validation.DbValidationError("Non retrouvé dans Select Case de PhilaEntities !", "Validation de la classe " & entityEntry.Entity.GetType.ToString & " non effectuée !"))
                End Select
     
            End If
     
     
            If result.ValidationErrors.Count > 0 Then
                Return result
            Else
                Return MyBase.ValidateEntity(entityEntry, items)
            End If
       End Function

    ce code marche très bien pour tout ajout (EntityState.Added) mais pas pour les modifications (EntityState.modified) car les GetType ne "fonctionnent plus" , exemple erreur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    System.Data.Entity.DynamicProxies.HistoAction_33F4B4452EB9B48D8D7617671B4CD5EBC47CCA7C366ABBECC1DB08FD54CC99C8
    Je pense que cela est du aux POCO
    Comment contourner ce problème, ou autre moyen de tester l'unicité ?
    par avance, merci
    Bertrand

  2. #2
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par 105rn2 Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    System.Data.Entity.DynamicProxies.HistoAction_33F4B4452EB9B48D8D7617671B4CD5EBC47CCA7C366ABBECC1DB08FD54CC99C8
    Je pense que cela est du aux POCO
    Comment contourner ce problème, ou autre moyen de tester l'unicité ?
    Dans ce cas il va falloir désactiver le proxy DbContext.Configuration.ProxyCreationEnabled = false;. Il faut noter qu'en désactivant le proxy cela désactive le Lazy Loading.

  3. #3
    Membre habitué
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2011
    Messages : 258
    Points : 126
    Points
    126
    Par défaut
    Re bonjour et merci pour la réponse,
    Puis je désactiver le proxy rien que durant cette cette phase de validation ?
    Ou sinon, comment fait on "habituellement" pour tester l'unicité ?
    Par avance, merci
    Bertrand

  4. #4
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par 105rn2 Voir le message
    Re bonjour et merci pour la réponse,
    Puis je désactiver le proxy rien que durant cette cette phase de validation ?
    Non. Tu dois désactiver le proxy avant chargement/modification des entités.

    Citation Envoyé par 105rn2 Voir le message
    Ou sinon, comment fait on "habituellement" pour tester l'unicité ?
    2 solutions que je connais peut-être il y en a d'autres :
    • Faire une requête Linq to Entities ou une procédure stockée (si la requête est complexe. La procédure stockée sera par la suite importée dans ton modèle) pour vérifier l'inexistence de la donnée concernée
    • Si l'unicité concerne d'autre colonnes autre que celle représentant la clef primaire alors ajouter une contrainte d'unicité au niveau de la base de donnée en spécifiant les colonnes concernées.

  5. #5
    Membre habitué
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2011
    Messages : 258
    Points : 126
    Points
    126
    Par défaut
    Re,
    je préfère garder les proxy (j'utilise le lazzy loading)
    Dans ma base SQL Server, j'ai crée mes contraintes d'unicité sur les champs qui ne sont pas des clés primaires (mes clés primaires sont toutes des IDxx)
    Le code que j'utilise vérifie pour les éléments ajoutés leur présence éventuelle dans la base locale et celle distante via requete Linq (toujours simple) mais ne marche pas pour des éléments modifiés
    Si je veux anticiper une exception de ma base SQL Server, ou puis je mettre mon test d'unicité : dans la classe elle même ?
    Merci encore de votre aide
    Bertrand

  6. #6
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par 105rn2 Voir le message
    Le code que j'utilise vérifie pour les éléments ajoutés leur présence éventuelle dans la base locale et celle distante via requete Linq (toujours simple) mais ne marche pas pour des éléments modifiés
    Bizarre... Y a-t-il un message d'erreur ?

    Citation Envoyé par 105rn2 Voir le message
    Si je veux anticiper une exception de ma base SQL Server, ou puis je mettre mon test d'unicité : dans la classe elle même ?
    Soit
    • Je redéfini ou crée une surcharge de la méthode SaveChanges de mon contexte
    • Je crée une méthode d’extension de mon contexte
    • Je crée une classe dérivant de mon contexte et qui implémente une nouvelle méthode


    Quel que soit ton choix, la nouvelle méthode devra d'abord faire appel à ta fonction de test d'unicité.

  7. #7
    Membre habitué
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2011
    Messages : 258
    Points : 126
    Points
    126
    Par défaut
    Bizarre... Y a-t-il un message d'erreur ?
    En fait, l'erreur est déclenchée par ma fonction
    Je fais le test au niveau du context général (au cas ou j'aurais à faire un test recouvrant plusieur classes), et chaque classe est déterminée/testée par un
    Code vb.net : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Select Case entityEntry.Entity.GetType
    Case GetType(Maclasse)
    ' test de maclasse
     Case Else
    ' mon message d'erreur pour signaler un nom de classe non trouvée
    En ajout ça marche très bien, mais en modification le Gettype ne retrouve pas le nom de ma classe car il est remplacé par des 3F4B4452EB9B48D8D7... du aux POCO si j'ai bien compris, d’où l'erreur que je peux bien entendu shunter, mais qui me signale que les test n'a pas été fait !

    Pour chaque classe crée par EF, j'ai crée une classe dérivée, ou je teste qq critères (lg min,..)
    Je viens d'essayer d'y ajouter un test d'unicité
    Code vb.net : 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
        Public Function Validate(validationContext As System.ComponentModel.DataAnnotations.ValidationContext) As System.Collections.Generic.IEnumerable(Of System.ComponentModel.DataAnnotations.ValidationResult) Implements System.ComponentModel.DataAnnotations.IValidatableObject.Validate
            ' seules les entitées modifiées ou ajoutées sont testées lors du saveChange
            ' les contraintes d'unicité sont gérées dans le context pour ajouts uniquement
            ' les tests nullables et longueur max sont faites par EF
     
            ' On profite de la validation pour enregistrer la date de modification
            DateModification = DateAndTime.Now
     
     
            If Pseudo.Length < 2 Then
                Return New List(Of ValidationResult) From {New ValidationResult("Le Pseudo doit faire au moins 2 caractéres" & vbLf & "Valeur erronée : " & Pseudo.ToString, {NomClass & " ID : " & ContactID, "Pseudo"})}
            End If
     
            If Not VerificationChaine.EmailouVide(Email) Then
                Return New List(Of ValidationResult) From {New ValidationResult("Le format de l'Email n'est pas valide !" & vbLf & "Valeur erronée : " & Email.ToString, {NomClass & " ID : " & ContactID, "Mail"})}
            End If
     
     
            ' Essai test unicité
            Dim NbreOccuranceLocal As Integer
            Dim NbreOccuranceBase As Integer
            Using ctxC As New PhilaEntities
     
                NbreOccuranceLocal = ctxC.Contacts.Local.Where(Function(l) l.Pseudo = Me.Pseudo).Count()
     
                If NbreOccuranceLocal > 1 Then
                    Return New List(Of ValidationResult) From {New ValidationResult("doublon base locale", {"Contact ID : " & ContactID, "Pseudo"})}
                Else
                    NbreOccuranceBase = ctxC.Contacts.Where(Function(l) l.Pseudo = Me.Pseudo).Count()
                    If NbreOccuranceBase > 0 Then
                        Return New List(Of ValidationResult) From {New ValidationResult("doublon base distante", {"Contact ID : " & ContactID, "Pseudo"})}
                    End If
                End If
     
                Return New List(Of ValidationResult)
            End Using
        End Function

    cela semble marcher sur les éléments modifiés
    Est ce la bonne approche ?
    je teste sur base locale et distante, est ce vraiment la peine ?
    le code peut il être amélioré ?

    Merci encore
    Bertrand

  8. #8
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par 105rn2 Voir le message
    En fait, l'erreur est déclenchée par ma fonction
    Je fais le test au niveau du context général (au cas ou j'aurais à faire un test recouvrant plusieur classes), et chaque classe est déterminée/testée par un
    Code vb.net : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    Select Case entityEntry.Entity.GetType
    Case GetType(Maclasse)
    ' test de maclasse
     Case Else
    ' mon message d'erreur pour signaler un nom de classe non trouvée
    En ajout ça marche très bien, mais en modification le Gettype ne retrouve pas le nom de ma classe car il est remplacé par des 3F4B4452EB9B48D8D7... du aux POCO si j'ai bien compris, d’où l'erreur que je peux bien entendu shunter, mais qui me signale que les test n'a pas été fait !
    Tu sais les types générés par le proxy dérivent de tes classes telles que tu les as définies. En récupérant le type tu effectues une deuxième vérification en récupérant la classe de base et là c'est sûr t'auras pas de problèmes.

    Citation Envoyé par 105rn2 Voir le message
    cela semble marcher sur les éléments modifiés
    Est ce la bonne approche ?
    Je pense que oui.

    Citation Envoyé par 105rn2 Voir le message
    je teste sur base locale et distante, est ce vraiment la peine ?
    Je pense que non si tu es sûr que la base locale et distante sont tout le temps identique. (Base je comprends base de données j'espère que c'est ça)

    Citation Envoyé par 105rn2 Voir le message
    le code peut il être amélioré ?
    Peut-être essayer de regarder la classe de base de tes types générés par le proxy pour voir si tu peux en tirer quelque chose pour améliorer encore les tests de validation.

  9. #9
    Membre habitué
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2011
    Messages : 258
    Points : 126
    Points
    126
    Par défaut
    Re,
    un peu dans le désordre :
    Je pense que non si tu es sûr que la base locale et distante sont tout le temps identique. (Base je comprends base de données j'espère que c'est ça)
    Etant débutant, j'utilise peut être les mauvais termes !
    Pour moi, la base distante est la base SQL Server
    la base locale est celle que j'ai crée dans mon appli par un query.load, exemple context.contact.local
    Si je n'ai pas sauvegardé mes modif/ajout les 2 peuvent différer, c'est pourquoi je faisais les 2 tests car si j'ajoute 2 éléments identiques sans sauvegarder entre les 2, j'obtiens une exception lors de la sauvegarde
    je vais voir si cela en vaut la peine ..

    les types générés par le proxy dérivent de tes classes telles que tu les as définies. En récupérant le type tu effectues une deuxième vérification en récupérant la classe de base et là c'est sûr t'auras pas de problèmes
    Désolé, mais là je suis dépassé !!
    Le type des classes ajoutées et modifiées sont apparemment différentes puisque n'obéissant pas au même getType
    J'avoue ne pas maitriser les POCO, et ne sait pas comment récupérer "la classe de base"
    Peut-être essayer de regarder la classe de base de tes types générés par le proxy
    idem !

    Merci encore !
    Bertrand

  10. #10
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par 105rn2 Voir le message
    Re,
    un peu dans le désordre
    Etant débutant, j'utilise peut être les mauvais termes !
    Pour moi, la base distante est la base SQL Server
    la base locale est celle que j'ai crée dans mon appli par un query.load, exemple context.contact.local
    Si je n'ai pas sauvegardé mes modif/ajout les 2 peuvent différer, c'est pourquoi je faisais les 2 tests car si j'ajoute 2 éléments identiques sans sauvegarder entre les 2, j'obtiens une exception lors de la sauvegarde
    je vais voir si cela en vaut la peine ..
    Ah ok ! Vérifie directement sur ta base SQL Server.

    Citation Envoyé par 105rn2 Voir le message
    Le type des classes ajoutées et modifiées sont apparemment différentes puisque n'obéissant pas au même getType
    J'avoue ne pas maitriser les POCO, et ne sait pas comment récupérer "la classe de base"
    Une explication pour comment faire ici.

  11. #11
    Membre habitué
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2011
    Messages : 258
    Points : 126
    Points
    126
    Par défaut
    Re,
    Oups, j'ai dit une bêtise :
    1) mon test effectué dans l'entity me protège bien d'un doublon "local" : 2 enregistrements identiques saisis à la suite sans save intermédiaire
    2) mon test dans la classe, ne m'en protège pas : j'ai une exception dans ce cas lors du savechange provenant de la BDD
    Je ne comprends pas pourquoi :
    Si je rentre un doublon par rapport à une valeur dans la base distante, j'en suis averti : le test est donc bien activé !
    Si je rentre un doublon par rapport à ma base locale (2 saisies identiques sans save),il ne détecte rien !

    Une explication pour comment faire ici.
    Merci pour le lien, je vais devoir creuser et essayer car à première vue, la réponse à l'instruction doit être donnée dans l'instruction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Dim userType = ObjectContext.GetObjectType(user.[GetType]())
    juste pour pour obtenir ... "user"
    Je vais creuser ..

    Merci encore
    Bertrand

  12. #12
    Membre habitué
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2011
    Messages : 258
    Points : 126
    Points
    126
    Par défaut
    Re Oups,
    La validation au niveau de la classe elle même, telle que je l'ai définie,empêche toute modification d'un enregistrement déjà existant (logique : déjà dans la BDD)
    Je vais donc laisser tomber cette approche, pour m'orienter vers la validation au niveau de l'entity ou je devrais pouvoir trier les éléments ajoutés de ceux modifiés
    Reste à comprendre comment extraire le nom de classe d'origine du nom de classe POCO !!
    Bertrand

  13. #13
    Membre habitué
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2011
    Messages : 258
    Points : 126
    Points
    126
    Par défaut
    Re,
    J'ai enfin trouvé comment extraire le nom de la classe d'origine :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    Dim EntiteATester As Type
            If entityEntry.State = EntityState.Modified Then
                EntiteATester = entityEntry.Entity.GetType
                EntiteATester = ObjectContext.GetObjectType(EntiteATester) ' retrouve nom classe d'origine
                Select Case EntiteATester
                    Case GetType(Contact)
                        ' validation contact
                    Case GetType(Personnage)
                        ' validation Personnage
                End Select
     
            End If
    Merci encore
    Bertrand

  14. #14
    Membre habitué
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    258
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Pas de Calais (Nord Pas de Calais)

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2011
    Messages : 258
    Points : 126
    Points
    126
    Par défaut
    Re Oups !
    j'ai clôturé ce post un peu trop rapidement !
    J'ai modifié ma validation en incluant les entités modifiées, sans penser que
    - lors d'un ajout la présence dans la BDD d'une entité identique est anormale
    - lors d'une modification par contre elle est normale
    Je pense donc compléter mon test par vérification ultérieure que les ID sont identiques lors d'une modification
    Est ce la bonne approche ?
    Le pb est que cela va générer 2 requêtes par validation,e t que j'ai des centaines de classes :-(
    Autre idée ?
    Bertrand

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [AJAX] Test d'unicité d'une valeur
    Par medessad dans le forum AJAX
    Réponses: 4
    Dernier message: 04/07/2012, 17h09
  2. [eZ Publish] Test l'unicité de la valeur d'un champs dans la base de contenu d'eZ
    Par tic_29 dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 2
    Dernier message: 27/10/2009, 15h02
  3. Script test de deux chaine avec if
    Par kacedda dans le forum Linux
    Réponses: 6
    Dernier message: 02/05/2003, 15h38
  4. [XMLRAD] test de nullité
    Par Pm dans le forum XMLRAD
    Réponses: 5
    Dernier message: 29/11/2002, 10h57
  5. test collisions
    Par tatakinawa dans le forum OpenGL
    Réponses: 5
    Dernier message: 08/06/2002, 06h03

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