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 :

Tenir compte du cache dans les requêtes


Sujet :

Entity Framework

  1. #1
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    716
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 716
    Points : 112
    Points
    112
    Par défaut Tenir compte du cache dans les requêtes
    Bonjour,

    J'ai créer des objets Repository (un objet pour chaque table) sur lesquels je fais mes requêtes.

    Exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this.EntitySet.Where(elt=> (elt.NoCommande == noCommande)).FirstOrDefault();
    L'objet EntitySet est de type DbSet.


    Lorsque je valide ma commande, je met à jour le montant du budget alloué a chaque ligne de commande.
    Je récupère le budget avec une requête similaire à celle ci-dessus et je le met à jour
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    budget.montant = budget.montant + mtCommande;
    this.Update(budget);
    Je ne fais la commande this.UnitOfWork.Save() qu'a la fin si tout est OK.

    Mon problème est que je peut être amené à mettre à jour plusieurs fois le même budget. Or lorsque je récupère le budget avec ma requête, cela récupère celui en base de données et ne tient pas compte de ma précédente mise à jour faîte dans le contexte.

    Comment faut il faire pour tenir compte des mises à jour faîte dans le contexte ?

    Merci pour votre aide.

  2. #2
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2010
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2010
    Messages : 64
    Points : 155
    Points
    155
    Par défaut
    Bonjour

    Que fais tu dans les méthodes this.Update() et this.UnitOfWork.Save(), je suppose que dans la deuxième tu fait un context.SaveChange() t que tous tes repository sont issus de ce même context?
    Il manque quelques infos mais de manière général tu récupere ton objet une fois via le repository ensuite tu fait tous les traitement metiers que tu as a faire.

    Prenons budget.montant vaut 5 en base comme assertion de départ
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    budget.montant = budget.montant + 1;
    budget.montant = budget montant + 2
    context.SaveChange();
    Ton budget.montant en base seras bien égal a 8

    Pour préciser ma réponse, au niveau de l'update Entity vas s'occuper de taggé ton objet budget comme modifier, avec toutes les anciennes valeurs (au cas ou on est besoin de rollBack) et les nouvelle valeurs.
    Quand tu appelle ContextSaveChange Entity vas verifier tous les objects taggé comme modifier et pousser l'update en base du coup tu n'as pas besoin de signifier d'update explicitement.

  3. #3
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    716
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 716
    Points : 112
    Points
    112
    Par défaut
    La fonction this.Update(budget); met à jour le budget dans le contexte uniquement.
    La fonction this.UnitOfWork.Save() fait la mise à jour dans la base de données.

    En fait, on peut voir le code un peut sous cette manière en beaucoup plus complexe.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    foreach (ligne in Commande)
    {
       traitement1();
       majBudget(codeBudget, montantBudget);
       traitement2();
    }
     
    void majBudget(String codeBudget, Int32 montantBudget)
    {
      Budget monBudget = this.EntitySet.Where(elt=> (elt.CodeBudget == codeBudget)).FirstOrDefault();
      monBudget.montant = monBudget.montant + montantBudget;
    }
    Voila un peu comment cela est dans le code en beaucoup plus complexe (lors d'une commande je vais mettre à jour le stock, le budget, les réceptions...). C'est pour cela que je vais à chaque fois chercher le budget correspond à la ligne, puis son stock, etc.. via des fonctions

    Lorsque je récupère monBudget et qu'il a été mis à jour dans le contexte, je récupère celui de la base de donnée et non pas celui qui est en cours de mise à jour...

    Comment faire pour le récupèrer ?
    Par exemple dans SqlPlus ou SqlDevelopper avec Oracle, je peux faire 10 requêtes et récupèrer les données qui sont en train d'être modifié sans problème alors qu'elles ne sont pas mise à jour dans la base de données (commit).


    Merci pour votre aide.

  4. #4
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2010
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2010
    Messages : 64
    Points : 155
    Points
    155
    Par défaut
    Comme je te l'ai dit précedement quand tu fait ca
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    monBudget.montant = monBudget.montant + montantBudget;
    ton objet est mis a jour dans le context tu n'as pas besoin de faire d'update explicite.

    Entity track tous les changement sur les objets que tu remonte de la base ( c'est transparent pour toi), pour la suite tu devrais passer les objets entre tes methodes ce qui te permet de garder les modifs précedente d'une part et de ne pas faire plusieur fois le meme appel en base alors que tu as deja l'objet (r.isque de problème de perf et mauvais pratique).

    Quand a la mise a a jour en base de donnée ca devrait ce résumer a une seul ligne context.SaveChange().
    En esperant répondera ta problèmatique.

  5. #5
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    716
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 716
    Points : 112
    Points
    112
    Par défaut
    Bonjour,

    Je comprend bien que passer mes objets de méthode en méthode va améliorer les performances (plus besoin de faire de select).

    Mais comment je dois faire ?
    Par exemple, je peux avoir une commande qui va impacter des dizaines de budgets, des dizaines de stock, etc... ! Je vais donc avoir des fonctions avec des centaines de paramètres ingérables...
    De plus comment je fait pour savoir que j'ai déjà rechercher tel budget et que donc je ne vais aller dans la base mais aller dans ma liste de budget ?
    Ma précédente application est en java, et je n'avais pas à faire cela, cela est transparent... Java allait récupérer le budget à jour dans le contexte.

    J'ai en effet vu aucune différence si j'ajoutais le Update(monBudget);.

    Mais pourquoi lorsque je vais rechercher mon budget, je n'obtiens pas celui mis à jour ? Comment faire pour l'obtenir à jour (il a été modifié dans le contexte et pas dans la bas de données) ?

    Merci pour votre aide.

  6. #6
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2010
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2010
    Messages : 64
    Points : 155
    Points
    155
    Par défaut
    Pourrais tu nous montrer le code de comment tu créer et peuple tes objets repository ainsi que le code de ta méthode this.UnitOfWork.Save().

  7. #7
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    716
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 716
    Points : 112
    Points
    112
    Par défaut
    Voici un exemple d'entité (simplifié) :

    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
     
    [DataContract]
    	public partial class Corr
    	{
    		public Corr()
    		{
    			this.LienBudgetCorr = new HashSet<BudgetCorr>();
    		}
     
    		public string CodeCorr { get; set; }
    		public string Libelle { get; set; }
    		public string TypeCorr { get; set; }
    		public string Adresse { get; set; }
    		public string Ville { get; set; }
    		public string CodePostal { get; set; }
    		public string CodePays { get; set; }
    		public virtual Pays LienPays { get; set; }
    Voici un exemple d'objet Repository :
    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
     
    public partial class CorrRepository : EntityRepository<Entities.Corr>, ICorrRepository
    	{
    		public CorrRepository()
    		{
    		}
     
    		public virtual Colbert.Entities.Corr GetByIds(String codeCorr)
    		{
    			try
    			{
    				return this.EntitySet.Where(elt=> (elt.CodeCorr == codeCorr)).FirstOrDefault();
    			}
    			catch(System.Exception ex)
    			{
    				return null;
    			}
    		}
    Les objets sont rempli à la demande comme dis précédemment :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Budget monBudget = this.EntitySet.Where(elt=> (elt.CodeBudget == codeBudget)).FirstOrDefault();
    Normalement je devrais obtenir automatiquement mon budget à jour même si je refais une requete ?

  8. #8
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2010
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2010
    Messages : 64
    Points : 155
    Points
    155
    Par défaut
    Edit: J'ai l'impression que j'avais mal compris le problème (mais dur dur sans code)

    Essaie ca ( en supposant que codeBudget soit ta clé primaire et un string)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     monBudget = this.EntitySet.Find(codeBudget);
    La doc de find dit
    A round-trip to the database will only be made if the entity with the given key is not found in the context.

    Voir ici http://msdn.microsoft.com/en-us/data/jj573936.aspx

    Tu peux aussi regarder du coté de DbSet.Local ici
    http://msdn.microsoft.com/en-us/libr...vs.103%29.aspx

  9. #9
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    716
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 716
    Points : 112
    Points
    112
    Par défaut
    Merci beaucoup, on se rapproche du résultat souhaité !

    Le Find me semble très bien lorsque je connais la clé primaire.



    Le DbSet.Local ne fonctionne pas pareil et c'est vraiment dommage. Il retourne quelque chose uniquement si des enregistrements ont été modifiés dans le contexte sinon rien. Donc s'il ne retourne rien, il faut aller faire la requête dans la base.
    On pourrait faire quelque chose de ce genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    if (requeteLocal.Any())
    {
    return requeteLocal.FirstOrDefault();
    }
    else
    {
    return requeteBase.FristOrDefault();
    }
    N'y a t'il pas quelque chose comme le Find pour les requêtes ?



    Par contre quand je n'utilise pas la clef primaire pour mettre à jour plusieurs données (5 lignes par exemple).
    Puis que je reviens sur ces données mais avec une plus grand amplitude (10 lignes par exemple).
    Comment dois je faire pour récupérer les 10 lignes en cours : c'est à dire les 5 lignes modifiées dans le contexte et les 5 autres de la base?

  10. #10
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2010
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2010
    Messages : 64
    Points : 155
    Points
    155
    Par défaut
    Le DbSet.Local ne fonctionne pas pareil et c'est vraiment dommage. Il retourne quelque chose uniquement si des enregistrements ont été modifiés dans le contexte sinon rien. Donc s'il ne retourne rien, il faut aller faire la requête dans la base.
    Ce n'est pas le comportement décrit par microsoft.

    This property returns an ObservableCollection<T> that contains all Unchanged, Modified, and Added objects that are currently tracked by the context for the given DbSet. The returned observable collection stays in sync with the underlying DbSet collection and the contents of the context. This means that you can modify the observable collection or add/remove entities to/from the underlying DbSet collection (that includes adding entities by executing a query) and both collections will be synchronized.

    Une des solution serait de charger tous les objets dont tu auras besoin en une seul fois dans ton context.Puis de travailler uniquement avec DbSet.Local et une fois que tous tes calculs metiers sont faits tu sauve en base.

    En plus en fesant comme ca tu garantit l'intégrité de tes données le SaveChange() créant une transaction ou s'inscrivant dans une transaction distribué existante; donc soit tous passe, soit rien.

  11. #11
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    716
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 716
    Points : 112
    Points
    112
    Par défaut
    Ce qui est difficile, c'est que je ne sais pas forcément ce qui va être mis à jour. Je ne peux pas récupérer toute la table, cela pourrait entraîner une baisse des performances.



    Y a t'il une manière de ne pas dupliquer les requêtes, pour ne pas avoir à faire cela dans le cas où je n'ai pas la clef primaire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    var requeteBase = this.EntitySet.Where(elt=> (elt.CodeCorr == codeCorr)).FirstOrDefault();
     
    var requeteLocal =this.EntitySet.Local.Where(elt=> (elt.CodeCorr == codeCorr)).FirstOrDefault();
    Dans le cas où la clef primaire est composé, dans quel ordre et comment dois je passer les paramètres à la fonction Find ?

    Merci.

  12. #12
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2010
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2010
    Messages : 64
    Points : 155
    Points
    155
    Par défaut
    Je ne suis pas sur de comprendre quand tu dit que tu ne sais pas a l'avance ce qui vas être modifié.

    Quand fais tu tes calculs? une fois que l'utilisateur a tous saisie et cela d'un seul coup?(a ce moment la tu sais le sobjets que tu auras besoin).
    Pourrais-je avoir quelques précisions sur comment ce déroule ton application, ca aiderais a faire les bon choix parce que j'ai l'impression que il y a peut-être un problème de conception

    Pour la clé composé Microsoft l'epliqueras mieux que moi :

    Entity Framework allows your entities to have composite keys - that's a key that is made up of more than one property. For example, you could have a BlogSettings entity that represents a users settings for a particular blog. Because a user would only ever have one BlogSettings for each blog you could chose to make the primary key of BlogSettings a combination of BlogId and Username. The following code attempts to find the BlogSettings with BlogId = 3 and Username = "johndoe1987":
    using (var context = new BloggingContext())
    {
    var settings = context.BlogSettings.Find(3, "johndoe1987");
    }
    Note that when you have composite keys you need to use ColumnAttribute or the fluent API to specify an ordering for the properties of the composite key. The call to Find must use this order when specifying the values that form the key.

  13. #13
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    716
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 716
    Points : 112
    Points
    112
    Par défaut
    Comme j'ai essayé de l'expliquer plus haut, au vu des lourds traitements possibles je fais une fonction qui va faire chaque traitement qui pourra être utiliser dans d'autres cas de figure.

    Par exemple, lors de la validation d'une commande, chaque ligne va être étudié et entrainer un traitement :

    Ligne 1 -> entraine une modification d'une liste d'objets en stock (objet 1 et objet 2 par exemple) : je fais un select sur la table stock de la liste des objets en base
    Ligne 2 -> entraine une modification d'une liste d'objets en stock (objet 2 et objet 3 par exemple) : je fais un select sur la table stock de la liste des objets en base -> problème le stock de l'objet 2 a été modifié précédemment

    Pour l'instant je n'ai pas ce cas car je n'ai pas trop avancé mais à mon avis je vais le rencontrer.

  14. #14
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2010
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2010
    Messages : 64
    Points : 155
    Points
    155
    Par défaut
    Dans l'exemple que tu me donne qu'est ce qui t'empeche de faire dans un premier temp la remontée de donnée.
    -Select de ta command
    -Pour chaque ligne Select des objets en stock qui t'interesse et ajout a une liste si il n'y sont pas deja .

    Dans un deuxième temp les traitements modification des tes objets (plusieurs modif sur le même objet si besoin)

    Dans un troisième temp tu sauve en base.

    PS: Sinon ta solution avec le if duplique les requetes mais devrait etre ok niveau perf ce ci ne coutant pas grand chose vus que tu attaque des données local.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    var requeteLocal =this.EntitySet.Local.Where(elt=> (elt.CodeCorr == codeCorr)).FirstOrDefault();

  15. #15
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    716
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 716
    Points : 112
    Points
    112
    Par défaut
    Merci, je n'ai pas trop compris par contre comment il faut définir l'ordre de la clé primaire lorsque celle-ci est composée de plusieurs champs avec le ColumnAttribute?

    Je pense qu'il faut faire quelque chose comme cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    public class UserBuilding
        {
            [Key, Column(Order = 0)]
            public int UserId { get; set; }
            [Key, Column(Order = 1)]
            public int BuildingId { get; set; }
            public int BuildingLevel { get; set; }
        }
    Je n'ai rien mis, j'ai pris l'ordre défini dans la base de données et cela me retourne bien l'enregistrement voulu. Ce n'est donc pas obligé de le faire ?

  16. #16
    Membre habitué
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Octobre 2010
    Messages
    64
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Octobre 2010
    Messages : 64
    Points : 155
    Points
    155
    Par défaut
    Pour définir une clé composite dans entity tu peux le faire de deux manièeres différentes soit avec des attributs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    [Key, Column(Order = 0)]
        public int AccountId1{ get; set; }
        [Key, Column(Order = 1)]
        public int AccountId2{ get; set; }
    Soit avec Fluent :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this.HasKey(k => new { k.AccountId1, k.AccountId2 } );
    Si tu veux faire un find sur une entity qui a une clé composite pour clé primaire tu doit mettre les propriétés dans le même ordre que lorsque elles ont été defnis soit ici.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    this.EntitySet.Find(AccountId1,AccountId2}
    J'espere que ca t'éclaire un peu

  17. #17
    Membre régulier
    Inscrit en
    Janvier 2006
    Messages
    716
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 716
    Points : 112
    Points
    112
    Par défaut
    Merci, j'ai donc mis cela en place dans mon traitement.

    Cependant je rencontre un autre problème :
    Lorsqu’une erreur se produit, on déclenche la méthode : this.UnitOfWork.Discard();.
    Si je prends le cas suivant :
    - Je lance mon traitement et je simule une erreur -> j’appelle Discard() ; -> jusque l’a tout va bien
    - Je lance une deuxième fois le même traitement et je simule une erreur -> j’appelle Discard() ; -> j’obtiens alors l’erreur suivante au moment de l’appel à Discard :
    InnerException = {"AcceptChanges ne peut pas continuer, car les valeurs de clés de l'objet sont en conflit avec un autre objet dans ObjectStateManager. Assurez-vous que les valeurs de clés sont uniques avant d'appeler AcceptChanges."}

    Apparemment le premier Discard n’a pas réinitialisé le contexte ?
    Comment peut-on faire pour que le contexte soit réinitialisé ?

    Merci pour votre aide.

Discussions similaires

  1. [MySQL] Utilisation du caractère "`" dans les requêtes
    Par PeZ dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 07/03/2006, 16h01
  2. Simuler l'opérateur "*" dans les requêtes
    Par soso78 dans le forum Access
    Réponses: 3
    Dernier message: 29/11/2005, 10h42
  3. Majuscules - minuscules dans les requêtes
    Par calogerogigante dans le forum Requêtes
    Réponses: 11
    Dernier message: 25/10/2005, 11h36
  4. Accents non pris en compte dans les requêtes SELECT
    Par YanK dans le forum Requêtes
    Réponses: 1
    Dernier message: 30/08/2005, 10h57
  5. Ne pas tenir compte des accents dans une requete
    Par zamanika dans le forum Installation
    Réponses: 8
    Dernier message: 08/11/2004, 19h49

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