none
Entity framework 6 - AsNoTracking RRS feed

  • Question

  • Bonjour.

    je suis sur un développement et ma compréhension d'entity framework 6 me fait défaut.

    J'ai une entité en BDD avec un champs IsGenere, booléen.

    via un select, je choisis cette entité et la parcours dans une boucle pour générer des fichiers pdf, puis je passe IsGenere a true

    pour ne pas repasser sur ce fichier.

    le soucis, c'est qu'aux tours de boucle suivant, je repasse sur la première entrée de mon entité.

    par exemple, si j'ai 50 fichiers différents à générer, ca va générer 50 fois le même.

    je peux régler le problème avec AsNoTracking(), mais alors cette fois je ne peux plus sauvegarder le passage de IsGenere à true.

    du coup les fois suivantes je régénérerai les 50 fichiers plutôt que seulement ceux que je n'ai jamais généré.

    Que puis-je faire?

    jeudi 19 mars 2015 14:32

Réponses

  • OK c'est ce que je pensais :) Normalement avec le "&& isimprime==false" vous ne devriez plus avoir de problème.

    Maintenant il y a plus simple en terme de code, voici une requête LINQ qui recherche tous les sous-dossiers dans vos dossiers dont le code est dans la liste et sont le sous-dossier est IsImprime à false.

    var sdossiers = from sdossier in mService.T_SOUS_DOSSIER
                    join dossier in mService.T_DOSSIER on sdossier.ID_DOSSIER equals dossier.ID_CODE
                    where IListIdCodes.Contains(dossier.ID_CODE) && sdossier.IsImprime == false
                    select new { Dossier = dossier, SousDossier = sdossier };
    
    foreach (var sdossier in sdossiers.ToList())
    {
        sdossier.SousDossier.IsImprime = true;
        mService.SaveChanges();
    }
    

    On provoque une seule requête SQL, on créé un objet anonyme qui contient notre sous-dossier avec son dossier "parent", on boucle dessus.

    S'il vous faut boucle sur les dossiers, alors plutôt que de faire une requête à chaque ID, faite quelque chose comme

    var doss = mService.T_DOSSIER.Where(d => IListIdCodes.Contains(d.ID_CODE)).ToList();

    Vous obtiendrez la liste des dossier dont le code se trouve dans IListIdCodes (EF transforme le List.Contains() par un opérateur IN), vous ne ferez qu'une seule requête.

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    • Marqué comme réponse Raslbol vendredi 20 mars 2015 14:37
    vendredi 20 mars 2015 13:34
  • J'ai ma propre réponse:

    dans une boucle, les requêtes sur une même table utilisant AsNoTracking fonctionnent tant 

    qu'il n'y a pas en dessous des requêtes sur cette même table sans le AsNoTracking.

    En gros: le AsNoTracking est ignoré pour toutes les requêtes de hiérarchie supérieure à celle où on ne l'a pas mis.

    solution. il faut faire une boucle pour les requêtes utilisant le AsNoTracking, puis une autre boucle EN DEHORS de la première pour les requêtes sans AsNoTracking qui serviront à la sauvegarde en BDD.

    • Marqué comme réponse Raslbol vendredi 20 mars 2015 11:30
    vendredi 20 mars 2015 10:58
  • désolé, je vais essayer de donner un exemple.

    mService = new BDDEntity();
    
    //Pour chaque Id_Code
    foreach(string lIdCode in lListeIdCodes)
    {
    	var lMonDossier = mService.T_DOSSIER.Where(p => p.ID_CODE == lIdCode).ToList();
    	lMonDossier.ForEach(lDossier =>
    	{
    		mService.T_SOUS_DOSSIER.Where(p => p.ID_DOSSIER == lDossier.ID_DOSSIER && p.IsImprime == false).ToList().ForEach(lSousDossier =>
    		{
    			imprimer(lSousDossier);
    			lSousDossier.isImprime = true;
    			mService.SaveChanges();
    		});
    	});
    }

    Voilà, j'avais fait quelque chose comme ça. et le soucis que j'ai rencontré c'est que si je repère par exemple 50 sous-dossiers, on fera 50 tours et on écrira 50 fois le même dossier. comme si cela ne tenait pas compte du changement de Id_Code.

    en ajoutant des AsNoTracking sur mes tables. cela résolvait ce problème. mais du coup le savechange n'avait rien à sauvegarder puisqu'on ne garde plus trace des changements.

    solution, 2 boucles différentes:

    List<string> lListeDesID;
    
    foreach(string lIdCode in lListeIdCodes)
    {
    	lListeDesID = new List<string>();
    	var lMonDossier = mService.T_DOSSIER.AsNoTracking().Where(p => p.ID_CODE == lIdCode).ToList();
    	lMonDossier.ForEach(lDossier =>
    	{
    		mService.T_SOUS_DOSSIER.AsNoTracking().Where(p => p.ID_DOSSIER == lDossier.ID_DOSSIER && p.IsImprime == false).ToList().ForEach(lSousDossier =>
    		{
    			imprimer(lSousDossier);
    			lListeDesID.Add(lSousDossier.ID_SOUS_DOSSIER);
    		});
    	});
    	foreach(string lIdSousDos in lListeDesID)
    	{
    		mService.T_SOUS_DOSSIER.Single(p => p.ID_SOUS_DOSSIER == lIdSousDos).isImprime = true;
    	}
    	mService.SaveChanges();
    }

    j'ai écrit cela à la va vite. veuillez m'excusez les éventuelles fautes de syntaxe.

    Je sort tout juste de mes études et je connais peu entity et c#, aussi on peux sûrement coder plus propre.



    • Marqué comme réponse Raslbol vendredi 20 mars 2015 11:40
    • Modifié Raslbol vendredi 20 mars 2015 13:13
    vendredi 20 mars 2015 11:39

Toutes les réponses

  • J'ai ma propre réponse:

    dans une boucle, les requêtes sur une même table utilisant AsNoTracking fonctionnent tant 

    qu'il n'y a pas en dessous des requêtes sur cette même table sans le AsNoTracking.

    En gros: le AsNoTracking est ignoré pour toutes les requêtes de hiérarchie supérieure à celle où on ne l'a pas mis.

    solution. il faut faire une boucle pour les requêtes utilisant le AsNoTracking, puis une autre boucle EN DEHORS de la première pour les requêtes sans AsNoTracking qui serviront à la sauvegarde en BDD.

    • Marqué comme réponse Raslbol vendredi 20 mars 2015 11:30
    vendredi 20 mars 2015 10:58
  • Bonjour,

    Je n'ai pas bien saisi votre problème, vous pourriez montrez un exemple de code de votre boucle qui vous posait problème en vous obligeant à utiliser AsNoTracking ?

    Car personnellement le AsNoTracking je ne m'en sers que pour des améliorations de performances quand je suis en lecture seule sur de gros volume. Donc une situation ou le AsNoTracking est nécessaire m'intéresse.

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    vendredi 20 mars 2015 11:19
  • désolé, je vais essayer de donner un exemple.

    mService = new BDDEntity();
    
    //Pour chaque Id_Code
    foreach(string lIdCode in lListeIdCodes)
    {
    	var lMonDossier = mService.T_DOSSIER.Where(p => p.ID_CODE == lIdCode).ToList();
    	lMonDossier.ForEach(lDossier =>
    	{
    		mService.T_SOUS_DOSSIER.Where(p => p.ID_DOSSIER == lDossier.ID_DOSSIER && p.IsImprime == false).ToList().ForEach(lSousDossier =>
    		{
    			imprimer(lSousDossier);
    			lSousDossier.isImprime = true;
    			mService.SaveChanges();
    		});
    	});
    }

    Voilà, j'avais fait quelque chose comme ça. et le soucis que j'ai rencontré c'est que si je repère par exemple 50 sous-dossiers, on fera 50 tours et on écrira 50 fois le même dossier. comme si cela ne tenait pas compte du changement de Id_Code.

    en ajoutant des AsNoTracking sur mes tables. cela résolvait ce problème. mais du coup le savechange n'avait rien à sauvegarder puisqu'on ne garde plus trace des changements.

    solution, 2 boucles différentes:

    List<string> lListeDesID;
    
    foreach(string lIdCode in lListeIdCodes)
    {
    	lListeDesID = new List<string>();
    	var lMonDossier = mService.T_DOSSIER.AsNoTracking().Where(p => p.ID_CODE == lIdCode).ToList();
    	lMonDossier.ForEach(lDossier =>
    	{
    		mService.T_SOUS_DOSSIER.AsNoTracking().Where(p => p.ID_DOSSIER == lDossier.ID_DOSSIER && p.IsImprime == false).ToList().ForEach(lSousDossier =>
    		{
    			imprimer(lSousDossier);
    			lListeDesID.Add(lSousDossier.ID_SOUS_DOSSIER);
    		});
    	});
    	foreach(string lIdSousDos in lListeDesID)
    	{
    		mService.T_SOUS_DOSSIER.Single(p => p.ID_SOUS_DOSSIER == lIdSousDos).isImprime = true;
    	}
    	mService.SaveChanges();
    }

    j'ai écrit cela à la va vite. veuillez m'excusez les éventuelles fautes de syntaxe.

    Je sort tout juste de mes études et je connais peu entity et c#, aussi on peux sûrement coder plus propre.



    • Marqué comme réponse Raslbol vendredi 20 mars 2015 11:40
    • Modifié Raslbol vendredi 20 mars 2015 13:13
    vendredi 20 mars 2015 11:39
  • Pas de problème, il faut bien débuter un jour :)

    Je ne comprends pas bien votre structure entre DOSSIER et SOUS_DOSSIER, vous êtes sur de votre condition "p.ID_SOUS_DOSSIER == lDossier.ID_SOUS_DOSSIER" car pour moi vous faîte une relation Un-à-un ce qui ne me parait pas logique. Ce ne serait pas plutôt votre sous-dossier qui pointe sur un ID du DOSSIER.

    Et dans votre condition WHERE vous ne pouvez pas ajouter "&& p.IsImprime == false" pour filtrer les dossiers que vous n'avez pas imprimé ?

    Car là il n'y a aucune raison pour que EF ne fonctionne pas correctement.

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    vendredi 20 mars 2015 12:52
  • Oui, désolé. j'ai écrit ce code exemple pour illustrer la situation assez vite car je suis au travail.. ce n'est pas le vrai. (je ne peut pas l'afficher) je corrige ça de suite.

     ----Voilà, c'est corrigé.

    encore désolé. c'est bête de donner un exemple avec autant d'erreurs.

    • Modifié Raslbol vendredi 20 mars 2015 13:12
    vendredi 20 mars 2015 13:09
  • OK c'est ce que je pensais :) Normalement avec le "&& isimprime==false" vous ne devriez plus avoir de problème.

    Maintenant il y a plus simple en terme de code, voici une requête LINQ qui recherche tous les sous-dossiers dans vos dossiers dont le code est dans la liste et sont le sous-dossier est IsImprime à false.

    var sdossiers = from sdossier in mService.T_SOUS_DOSSIER
                    join dossier in mService.T_DOSSIER on sdossier.ID_DOSSIER equals dossier.ID_CODE
                    where IListIdCodes.Contains(dossier.ID_CODE) && sdossier.IsImprime == false
                    select new { Dossier = dossier, SousDossier = sdossier };
    
    foreach (var sdossier in sdossiers.ToList())
    {
        sdossier.SousDossier.IsImprime = true;
        mService.SaveChanges();
    }
    

    On provoque une seule requête SQL, on créé un objet anonyme qui contient notre sous-dossier avec son dossier "parent", on boucle dessus.

    S'il vous faut boucle sur les dossiers, alors plutôt que de faire une requête à chaque ID, faite quelque chose comme

    var doss = mService.T_DOSSIER.Where(d => IListIdCodes.Contains(d.ID_CODE)).ToList();

    Vous obtiendrez la liste des dossier dont le code se trouve dans IListIdCodes (EF transforme le List.Contains() par un opérateur IN), vous ne ferez qu'une seule requête.

    Cordialement,


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    • Marqué comme réponse Raslbol vendredi 20 mars 2015 14:37
    vendredi 20 mars 2015 13:34
  • Un +1 Pour se code optimisé.

    Je le garde au chaud pour utilisation ultérieure (ce qui ne saurait tarder).

    j'essayerai aussi de le refaire au calme. Un grand merci.

    vendredi 20 mars 2015 14:41
  • De rien :)


    Yan Grenier

    Merci de bien vouloir "Marquer comme réponse", les réponses qui ont répondues à votre question, et de noter les réponses que vous avez trouvé utiles.

    vendredi 20 mars 2015 14:42