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

Linq Discussion :

Problème avec Linq to Entities!


Sujet :

Linq

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé Avatar de MicaelFelix
    Profil pro
    Étudiant
    Inscrit en
    Juillet 2006
    Messages
    254
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juillet 2006
    Messages : 254
    Par défaut Problème avec Linq to Entities!
    EDIT 1 : Ça marche mais j'ai encore quelques problèmes par rapport à l'EF en général (voir questions en bas de ce message)

    ----------

    Bonjour!

    Je suis assez déçu par Linq & Entities Framework... oui, car très récemment j'ai commencé un projet, basé sur moins d'une dizaine de tables avec des contraintes bien définies dans la base de données, bref une bonne base.

    J'ai fait plusieurs tests avec et comme ceux ci s'avéraient être des succès j'ai décidé de l'utiliser vraiment dans mon projet en faisant tout avec l'Entities Framework (ou plutôt disons toute la partie simple des requêtes, certaines requêtes ont été laissées en SQL pour plus de facilité et de lisibilité car les LEFT JOIN sont certainement plus simple à mettre en place par des ordres SQL, et plus performantes si on les écrit bien).

    Tout allait bien jusqu'au moment où j'ai heurté le premier problème :
    L'exception de l'objet Entities Context qui devenait instable. Une petite recherche sur internet m'a permis de comprendre qu'il valait mieux avoir un Context global pour éviter les problèmes. C'est ce que j'ai fait (même si d'un côté technique cela enlève assez le principe de transaction qui était vraiment intéressant et apporté par l'EF (entities framework)).

    Et récemment, un problème incompréhensible s'est mis à s'afficher juste au pire moment car tout le transfert est déjà effectué vers ce système et impossible de revenir en arrière :
    Des erreurs de contraintes d'intégrité invalides sont apparues soudainement SANS modification de la base de données.
    Lorsqu'on travaille avec des bases de données de plus de 10 000 entrées et sur un projet qui est utilisé quotidiennement, j'avoue que je n'ai pas apprécié ce "coup bas" soudain (car cela fonctionnait très bien avant).

    Après plusieurs recherches sur internet, toujours rien... frustration totale, à chaque ajout de nouvelle entité dans le contexte, le SaveChange() envoie une exception comme quoi une contrainte d'intégrité n'est pas respectée, ce qui n'est pas le cas.

    La solution? Aucune en Entities Framework jusqu'à maintenant... personne ne semble avoir ce problème...
    Les réponses que j'ai eu jusqu'à présent sont des questions par rapport à la validité de mon ajout dans le contexte ou des relations d'intégrité invalides, malheureusement ce sont des questions que je me suis déjà posé et la réponse est toujours que la base de données est intacte et fonctionnelle.

    Donc, j'ai du à chaque .AddTo<nomdel'entité>(<instance de l'entité>) me faire une requête SQL similaire.
    Bien entendu cela fonctionne parfaitement car comme je l'ai dit la base de données elle même ne pose aucun problème, mais j'ai donc du code "sale" : la moitié en Entities Framework, et la moitié en SQL juste au moment des ajouts.

    Il est à noter que les modifications sur les entités déjà présentes ne semblent pas poser de problème lors de l'appel de SaveChange, ce sont simplement les ajouts de nouvelles entités dans le contexte qui posent problème.

    Assez parlé de ma frustration, passons au code pour vous montrer le problème (au cas où quelqu'un trouverait la solution) :

    Le meilleur des exemples est :
    Code : 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
    using System;
     
    using System.Collections.Generic;
     
    using System.Linq;
     
    using System.Web;
     
    using System.Web.UI;
     
    using System.Web.UI.WebControls;
     
    using palarcdbModel;
     
     
     
    public partial class AS_Default2 : System.Web.UI.Page
     
    {
     
        protected void Page_Load(object sender, EventArgs e)
     
        {
     
            WSUser x = new WSUser();
     
            x.Email = "myemaill@myprovider.com";
     
            x.Language = "en";
     
            GB.Context.AddToWSUsers(x);
     
            GB.Context.SaveChanges();
     
        }
     
    }
    Ce code échouera. Il n'y a aucun lien NON NULL qui pointe vers une autre table comme le définit le script de la table ci dessous (et vérifié plusieurs fois par d'autres méthodes) :
    Code : 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
    CREATE TABLE [palarcdbu].[WSUser](
    	[IDUser] [int] IDENTITY(1,1) NOT NULL,
    	[Email] [nvarchar](150) NOT NULL,
    	[Language] [nvarchar](2) NOT NULL,
    	[DtCreation] [datetime] NOT NULL,
    	[FlushNextTime] [bit] NOT NULL,
    	[IP] [nvarchar](255) NOT NULL,
    	[Password] [nvarchar](20) NOT NULL,
    	[SecureMD5] [nvarchar](50) NULL,
    	[Referer] [nvarchar](50) NULL,
    	[LastVersion] [nvarchar](10) NULL,
    	[LastDtActivity] [datetime] NULL,
    	[NbActivation] [int] NOT NULL,
    	[NbNewsletter] [int] NOT NULL,
    	[TempValue] [bit] NOT NULL,
    	[DoNotSend] [bit] NOT NULL,
    	[IDLanguage] [int] NULL,
     CONSTRAINT [PK_tblWSUser] PRIMARY KEY CLUSTERED 
    (
    	[IDUser] ASC
    )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
     
    GO
     
    ALTER TABLE [palarcdbu].[WSUser]  WITH CHECK ADD  CONSTRAINT [FK_WSUser_Language] FOREIGN KEY([IDLanguage])
    REFERENCES [palarcdbu].[Language] ([IDLanguage])
    GO
     
    ALTER TABLE [palarcdbu].[WSUser] CHECK CONSTRAINT [FK_WSUser_Language]
    GO
     
    ALTER TABLE [palarcdbu].[WSUser] ADD  CONSTRAINT [DF_tblWSUser_FlushNextTime]  DEFAULT ((1)) FOR [FlushNextTime]
    GO
     
    ALTER TABLE [palarcdbu].[WSUser] ADD  CONSTRAINT [DF_Table1_NbDownloads]  DEFAULT ((0)) FOR [NbActivation]
    GO
     
    ALTER TABLE [palarcdbu].[WSUser] ADD  CONSTRAINT [DF_tblWSUser_NbNewsletters]  DEFAULT ((0)) FOR [NbNewsletter]
    GO
     
    ALTER TABLE [palarcdbu].[WSUser] ADD  CONSTRAINT [DF_tblWSUser_TempValue]  DEFAULT ((0)) FOR [TempValue]
    GO
     
    ALTER TABLE [palarcdbu].[WSUser] ADD  CONSTRAINT [DF_Table1_NoCommunication]  DEFAULT ((0)) FOR [DoNotSend]
    GO
    Pour finir, voici l'erreur qui est affichée :
    Server Error in '/palarc' Application.<br/>
    Entities in 'Entities.WSUser_Detail' participate in the 'FK_WSUser_Detail_WSCountry' relationship. 0 related 'WSCountry' were found. 1 'WSCountry' is expected.

    That happens when it's on the line 17 :

    Line 17: GB.Context.SaveChanges();


    The stacktrace is :

    [UpdateException: Entities in 'Entities.WSUser_Detail' participate in the 'FK_WSUser_Detail_WSCountry' relationship. 0 related 'WSCountry' were found. 1 'WSCountry' is expected.]

    System.Data.Mapping.Update.Internal.RelationshipConstraintValidator.ValidateConstraints() +424

    System.Data.Mapping.Update.Internal.UpdateTranslator.ProduceCommands() +59

    System.Data.Mapping.Update.Internal.UpdateTranslator.Update(IEntityStateManager stateManager, IEntityAdapter adapter) +210

    System.Data.EntityClient.EntityAdapter.Update(IEntityStateManager entityCache) +117

    System.Data.Objects.ObjectContext.SaveChanges(Boolean acceptChangesDuringSave) +453

    System.Data.Objects.ObjectContext.SaveChanges() +9

    AS_Default2.Page_Load(Object sender, EventArgs e) in c:\Programmation\Sites web\palarc\AS\Default2.aspx.cs:17

    System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +14

    System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +35

    System.Web.UI.Control.OnLoad(EventArgs e) +99

    System.Web.UI.Control.LoadRecursive() +50

    System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +627
    Pourquoi l'Entities framework décide que les contraintes ne sont pas respectées sur un objet sur lequel je ne travaille pas? Aucune idée!
    [UpdateException: Entities in 'Entities.WSUser_Detail' participate in the 'FK_WSUser_Detail_WSCountry' relationship. 0 related 'WSCountry' were found. 1 'WSCountry' is expected.]
    WSUser_Detail est une autre table de ma base de données, je ne travaille pas dessus dans la page aspx (_Default2) mentionnée ci-haut, cette erreur ne devrait donc pas apparaitre.

    Je viens d'avoir un déclic, cela pourrait être relié à une autre entitée ajoutée au contexte mais qui était invalide (car j'ai effectivement eu un problème de par rapport à la FK sur WSCountry lors d'un ajout de WSUser_Detail).

    Mais le pool a été arrêté et relancé plusieurs fois, aucune trace de l'Entity Context instable ne devrait être restée sur le serveur!

    Y-a t'il une sauvegarde "réelle" du contexte gardée sur le serveur en tout temps même lors du stoppage de l'application web sur le serveur IIS? Là est la question!
    Je continue mes tests au cas où c'est effectivement ça... si quelqu'un peut m'éclairer au niveau du stockage des données du Context par l'Entity Framework cela serait vraiment apprécié.

    À bientôt et merci d'avance!


    EDIT 1 :
    Le problème original a été trouvé, un objet (entité) invalide (car incomplet) avait été ajouté dans le context mais je ne l'avais pas compris car c'était une erreur qui datait de plusieurs heures... et ensuite chacun de mes SaveChanges() sur d'autres tables échouaient car le contexte avait bien ajouté ET gardé en mémoire l'ajout d'entité invalide (ce qui est assez étrange car peu pratique mais bon... ça fonctionne comme ça apparemment!)
    Mes problèmes actuels sont :
    - Mon objet étant créé momentanément, après l'erreur il m'est impossible de rectifier cet enregistrement car il est invalide ET ajouté dans la liste des WSUser, comment faire pour supprimer les enregistrements invalides?
    - Comment faire pour "résoudre" un contexte qui a un état invalide? Exemple GB.Context.CheckAndCancelBadObjects() (bon ok je rêve un peu mais vous voyez le principe?)

  2. #2
    Rédacteur
    Avatar de The_badger_man
    Profil pro
    Développeur .NET
    Inscrit en
    Janvier 2005
    Messages
    2 745
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Janvier 2005
    Messages : 2 745
    Par défaut
    Concernant ton code, c'est quoi GB.Context ? ça sort d'où ? comment est créé le context ?

    En général il est recommandé d'avoir un context avec une durée de vie assez courte pour éviter des problème:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    (using MonContext context = new MonContext())
    {
           WSUser x = new WSUser();
            x.Email = "<a href="mailto:myemaill@myprovider.com">myemaill@myprovider.com</a>";
            x.Language = "en";
            context.AddToWSUsers(x);
            context.SaveChanges();
     }
    Les règles du forum
    Le trio magique : FAQ + Cours + fonction rechercher
    Mes articles
    Pas de questions par messages privés svp

    Software is never finished, only abandoned.

  3. #3
    Rédacteur
    Avatar de Louis-Guillaume Morand
    Homme Profil pro
    Cloud Architect
    Inscrit en
    Mars 2003
    Messages
    10 839
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Cloud Architect
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2003
    Messages : 10 839
    Par défaut
    Ton problème de contrainte invalide m'a rendu fou avec EF et n'étant plus un débutant, j'étais convaincu que l'erreur venait du framework.
    Néanmoins, tu le verras par la suite, si ta base est à jour, que ton designer EF aussi, EF ne se trompe JAMAIS. et il y a effectivement une contrainte qui est invalide quelque part. Tu as sûrement dû chercher mais je suis quasi certain que l'erreur est quelque part chez toi, surtout si t'as pas touché à la base

  4. #4
    Membre éclairé Avatar de MicaelFelix
    Profil pro
    Étudiant
    Inscrit en
    Juillet 2006
    Messages
    254
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juillet 2006
    Messages : 254
    Par défaut
    Citation Envoyé par The_badger_man Voir le message
    Concernant ton code, c'est quoi GB.Context ? ça sort d'où ? comment est créé le context ?

    En général il est recommandé d'avoir un context avec une durée de vie assez courte pour éviter des problème:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    (using MonContext context = new MonContext())
    {
     }

    Voici une partie de la class GB :
    Code : 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
    public class GB
    {
        static Object oLocker = new Object();
        private GB()
        { }
     
        private static Entities _CurrentContext;
        public static Entities Context
        {
            get
            {
                if (_CurrentContext == null)
                    _CurrentContext = new Entities();
                return _CurrentContext;
            }
            set { _CurrentContext = value; }
        }
    Le GB.Context est un objet static qui dure relativement longtemps.
    Ce que tu me donnes comme exemple est simple, mais dans mon cas j'ai plusieurs classes appelées et je n'avais pas envie de leur passer en paramètre le Context à chaque fonction appelée... pour des raisons de simplicité.
    De plus j'ai vu que déclarer un context global sauve justement des erreurs car EF gère mal les contextes trop nombreux.
    D'ailleurs c'est bien le problème que j'avais : avant de passer à cette méthode globale j'instanciais un nouveau context à chaque groupe d'opérations, ce qui m'a donné plusieurs erreurs fatales et j'ai dû trouver une autre solution.

    Citation Envoyé par Louis-Guillaume Morand Voir le message
    Ton problème de contrainte invalide m'a rendu fou avec EF et n'étant plus un débutant, j'étais convaincu que l'erreur venait du framework.
    Néanmoins, tu le verras par la suite, si ta base est à jour, que ton designer EF aussi, EF ne se trompe JAMAIS. et il y a effectivement une contrainte qui est invalide quelque part. Tu as sûrement dû chercher mais je suis quasi certain que l'erreur est quelque part chez toi, surtout si t'as pas touché à la base
    Malheureusement non, comment pourrais-je avoir une base de données invalide? J'ai vérifié toutes les lignes de la table et aucun IDCountry n'a été oublié dans la table WSUser_Detail.
    De toutes façons tout est géré par contraintes dans mes tables, il n'y a donc pas de possibilité d'erreur d'inscription dans la BD.
    Après, pour le problème du Designer EF non à jour, crois moi je n'oublie jamais de le mettre à jour et je l'ai remis à jour plusieurs fois pour être sûr qu'il n'y avait pas de différence suite à cette erreur.

    Dans mon premier message je disais que j'avais peut être trouvé la solution... en fait... pas vraiment.
    Je m'explique : effectivement à un moment donné dans le projet il y a eu une erreur avec l'IDCountry mal réglé. C'est déjà ça ça veut dire que l'erreur originalement causée par moi, et non pas EF
    Mon problème c'est que même après l'arrêt de l'application et son relancement, le Context ne semblait pas avoir été supprimé.
    Est-ce que l'arrêt de l'application ne provoque pas le recyclage du pool et donc l'arrêt de tous les Context utilisés (1 ici)?

    Voilà, maintenant ça commence à refonctionner!
    Je viens de comprendre donc ce qui se passe réellement :
    1. Un objet myNewUser est créé
    2. Les données sont remplies, mais une erreur de contrainte d'intégrité est à l'intérieur (exemple : oubli d'un champ NON NULL dans la base de données)
    3. L'objet bien qu'invalide est inséré avec GB.Context.AddToWSUsers(myNewUser);
    4. Le context échoue l'insertion lors du .SaveChanges()
    5. Le contexte ne supprime aucun des éléments invalides (ce myNewUser), le contexte refuse donc TOUT appel de SaveChanges() car dans sa mémoire on veut lui ajouter un myNewUser invalide


    C'est beaucoup plus clair pour moi!

    Mes problèmes actuels sont :
    - Mon objet étant créé momentanément, après l'erreur il m'est impossible de rectifier cet enregistrement car il est invalide ET ajouté dans la liste des WSUser, comment faire pour supprimer les enregistrements invalides?
    - Comment faire pour "résoudre" un contexte qui a un état invalide? Exemple GB.Context.CheckAndCancelBadObjects() (bon ok je rêve un peu mais vous voyez le principe?)
    - Devrais-je utiliser des TransactionScope (comme montré ici : http://stackoverflow.com/questions/8...ceptallchanges) pour tenter l'ajout et en cas d'erreur annuler le tout avant que le contexte soit sauvegardé?

    Je vais vérifier si le TransactionScope peut régler le problème en annulant les objets invalides d'un contexte... par contre je ne sais pas si son utilisation est conseillée dans le cas d'un contexte global comme je le fais!

    Merci à vous deux pour votre aide.

  5. #5
    Membre éclairé Avatar de MicaelFelix
    Profil pro
    Étudiant
    Inscrit en
    Juillet 2006
    Messages
    254
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juillet 2006
    Messages : 254
    Par défaut
    Citation Envoyé par Louis-Guillaume Morand Voir le message
    et n'étant plus un débutant, j'étais convaincu que l'erreur venait du framework.
    C'est exactement ce que j'ai ressenti face aux problèmes qui apparaissaient "après" la mise en service, d'où l'origine de ce message
    Le problème c'est que EF cache bien ses choses, si seulement j'avais su ce qu'il essayait de faire à l'interne cela m'aurait simplifié la vie
    Je suis peut être trop habitué aux erreurs "simples" du style SQL (INSERT/UPDATE) qui fonctionnent ou ne fonctionnent pas du tout

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

Discussions similaires

  1. Réponses: 1
    Dernier message: 06/09/2011, 21h26
  2. [Entity Framework] Insertion de donnée avec LINQ to Entities
    Par Leelith dans le forum Framework .NET
    Réponses: 15
    Dernier message: 05/11/2009, 22h56
  3. problème avec linq (modifier une table)
    Par Dr.ASAKURA dans le forum Linq
    Réponses: 1
    Dernier message: 06/08/2009, 03h14
  4. Réponses: 3
    Dernier message: 09/06/2009, 10h08
  5. Problème avec Linq
    Par watiero dans le forum Général Dotnet
    Réponses: 0
    Dernier message: 04/12/2008, 23h25

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