Forum |  HardWare.fr | News | Articles | PC | S'identifier | S'inscrire | Shop Recherche
1609 connectés 

  FORUM HardWare.fr
  Programmation
  C#/.NET managed

  Entity Framework - Doublon dans la DB

 


 Mot :   Pseudo :  
 
Bas de page
Auteur Sujet :

Entity Framework - Doublon dans la DB

n°2267484
fredo3
Posté le 10-10-2015 à 14:33:59  profilanswer
 

Bonjour
 
J'essaye de me faire un peu la main sur Entity Framkework. l'API a un comportement pour le moins bizarre, ou du moins que je ne comprends pas. Du coup je me demande si je ne me serais pas trompé quelque part.
 
J'explique.
 
Imaginez que vous ayez une entity "Contact" et une "Type". Un contact peut avoir un type. Et un type peut être associé à plusieurs contacts, forcément.
 
Alors ce que je ne comprends pas c'est que quand j'associe un objet "contact" avec un objet "type" déjà présent en db (bref qui a déjà un id), EF injecte un contact en base de donnés, très bien, sauf qu'en même temps il me réinjecte un nouveau objet "Type" en base de donnés aussi (alors qu'il y est déjà présent!).
 
Je me suis trompé quelque part dans la config du bazar?
 
Merci

mood
Publicité
Posté le 10-10-2015 à 14:33:59  profilanswer
 

n°2267646
TotalRecal​l
Posté le 13-10-2015 à 13:54:36  profilanswer
 

La question est : comment tu lis ton type existant et ton nouveau contact dans ton code.
Commence par montrer comment tu écris dans ta base, je ne vois pas comment on pourrait t'aider à corriger ton code sans le voir :sarcastic:...


Message édité par TotalRecall le 13-10-2015 à 13:55:12

---------------
Topic .Net - C# @ Prog
n°2267727
fredo3
Posté le 14-10-2015 à 16:15:46  profilanswer
 

C'est basé en grande partie sur cette DAL:
http://dotnetspeak.com/2013/03/vs- [...] as-vegas-2

 


Mais je crois que je viens de comprendre d'où viens le problème, et ça me fout les boules.
Je ne sais pas pourquoi sur sa démo à lui en EF5 ca marche, alors que moi en EF6 ça ne marche pas, alors que je n'ai pas fait de grosse différence au niveau du code.

 


Je me rends compte en fait que dès qu'on associe un objet à un autre, il faut obligatoirement le faire au sain d'un context. (using...).
Si les objets sont associés en-dehors du context ou dans 2 context différents, le lien ne se fait pas correctement.

 

Mais du coup comment on fait pour ne pas utiliser de context dans la couche Business???


Message édité par fredo3 le 14-10-2015 à 16:21:47
n°2267728
TotalRecal​l
Posté le 14-10-2015 à 16:17:26  profilanswer
 

Je ne comprend pas la moitié de ce que tu dis.
On veut voir TON code, la partie utile. Vu ton use case décrit au début ça ne doit pas faire plus de 30 lignes.


---------------
Topic .Net - C# @ Prog
n°2267729
fredo3
Posté le 14-10-2015 à 16:22:21  profilanswer
 

Il y a une vingtaine de classes et dizaine d'interfaces pour la DAL... alors bon...


Message édité par fredo3 le 14-10-2015 à 16:22:30
n°2267734
TotalRecal​l
Posté le 14-10-2015 à 16:39:12  profilanswer
 

Y a pas 50000 façons de charger ou désigner une entité existante par son id, pour la lier à une autre et déclencher la persistance.  
C'est ça dont on a besoin pour t'aider, pas du brol autour.  
Perso là j'ai pas trop envie de debugger 16,5Mo de sources d'un autre qui ne posent même pas le problème que tu espères nous voir résoudre...


---------------
Topic .Net - C# @ Prog
n°2267738
fredo3
Posté le 14-10-2015 à 18:09:42  profilanswer
 

Bon okay.
 
Alors dans la couche Access layer, pour tous ce qui est d'opération d'écriture il y a cette classe abstraite "WriteRepository":

Code :
  1. public abstract class WriteRepository<TContext> : IWriteRepository where TContext : DbContext, new()
  2.     {
  3.         private readonly TContext _context;
  4.         protected TContext Context { get { return _context; } }
  5.         protected WriteRepository()
  6.         {
  7.             _context = new TContext();
  8.         }
  9.         public void Dispose()
  10.         {
  11.             _context.Dispose();
  12.         }
  13.         public TItem Update<TItem>(TItem item, bool saveImmediately = true) where TItem : class, new()
  14.         {
  15.             return PerformAction(item, EntityState.Modified, saveImmediately);
  16.         }
  17.         public TItem Delete<TItem>(TItem item, bool saveImmediately = true) where TItem : class, new()
  18.         {
  19.             return PerformAction(item, EntityState.Deleted, saveImmediately);
  20.         }
  21.         public TItem Insert<TItem>(TItem item, bool saveImmediately = true) where TItem : class, new()
  22.         {
  23.             return PerformAction(item, EntityState.Added, saveImmediately);
  24.         }
  25.         public void Save()
  26.         {
  27.             _context.SaveChanges();
  28.         }
  29.         protected virtual TItem PerformAction<TItem>(TItem item, EntityState entityState, bool saveImmediately = true) where TItem : class, new()
  30.         {
  31.             _context.Entry(item).State = entityState;
  32.             if (saveImmediately)
  33.             {
  34.                 _context.SaveChanges();
  35.             }
  36.             return item;
  37.         }


 
Elle est abstraite, générique et est implémentée par tous les classes repositories. Exemple celle des catégories:

Code :
  1. public class CategoryRepository : WriteRepository<AppContext>, ICategoryRepository
  2.     {
  3.         public IEnumerable<Category> GetCategories(ICriteria criteria)
  4.         {
  5.             IQueryable<Category> query = Context.Categories;
  6.             if (criteria.IsSearch)
  7.             {
  8.                 var value = criteria.GetFieldData(criteria.FilterColumn);
  9.                 query = query.Where(one => one.Name.Contains(value));
  10.             }
  11.             if (criteria.SortColumn == "Name" && criteria.SortOrder == "asc" )
  12.             {
  13.                 query = query.OrderBy(one => one.Name);
  14.             }
  15.             else if (criteria.SortColumn == "Name" && criteria.SortOrder == "desc" )
  16.             {
  17.                 query = query.OrderByDescending(one => one.Name);
  18.             }
  19.             else
  20.                 query = query.OrderBy(one => one.Name);
  21.             query = query.Skip((criteria.PageIndex - 1) * criteria.PageSize).Take(criteria.PageSize);
  22.             return query;
  23.         }
  24.         public int GetTotalCategories()
  25.         {
  26.             return Context.Categories.Count();
  27.         }
  28.         public Category GetCategoryById(int id)
  29.         {
  30.             return Context.Categories.FirstOrDefault(one => one.CategoryId == id);
  31.         }
  32.     }

 
Comme tu le vois cette classe repository dispose de méthodes permettant de charger des objets présents en db.
 
Au dessus de tout ça, il y la couche Business qui dispose de classes de service. Ces classes de service sont instanciées à l'aide de StructureMap pour une meilleure isolation entre les couches. Mais rien de spécial de ce côté elles ne font que faire appel aux méthodes des classes repository correspondantes.
 
Bref en gros l'opération que j'exécute pour vérifier si l'association entre l'object "Contact" et l'objet "Category" se fait bien est la suivante:

Code :
  1. Data.Category cat = new Data.Category() { Name = "My new Category" };
  2.             CategoryService catService = new CategoryService();
  3.             catService.CreateCategory(cat);
  4.             //cat = catService.GetCategory(1);
  5.             Data.Contact contact = new Data.Contact { Name = "test name 1" };
  6.             contact.Category = cat;
  7.             ContactService contactService = new ContactService();
  8.             contactService.CreateContact(contact);


 
Résultat, la cat est présente 2 fois en db.
 
Alors que si je n'utilise pas la DAL, et que j'utilise le classique:

Code :
  1. using (var cxt = new AppContext())
  2.             {
  3. ...
  4. ...
  5.             }


Et que j’effectue les opérations dedans, ça marche correctement.
Alors il y a bien la méthode "Attache" du context qui permet justement de rattacher un objet déconnecté du context. Mais ça fait limite usine à case par la suite.
Il y a aussi la méthode "AddOrUpdate", mais elle effectue une opération de lecture en db à chaque fois pour vérifier si l'objet est bien présent ou pas, bref usine à gaz aussi.


Message édité par fredo3 le 14-10-2015 à 18:30:12
n°2267754
TotalRecal​l
Posté le 15-10-2015 à 09:10:41  profilanswer
 

Au début de ton code tu as déjà ta catégorie en base, ou bien tu en as 0 ?

 

Si elle existe déjà et que Name sur catégorie n'est pas ta PK comment tu veux que le système comprenne qu'il doit prendre celle qui existe et pas en générer une nouvelle ?
Pour lui tu es bien en train de chercher à créer une nouvelle catégorie. Pour attacher une existante il faut la charger par son id, ou bien écrire directement dans le champ FK_Categorie de Contact.

 

Si par contre tu dis que ce code là te génère 2 catégories alors que tu en avais 0, je ne sais quoi te dire.


Message édité par TotalRecall le 15-10-2015 à 09:11:06

---------------
Topic .Net - C# @ Prog
n°2267760
fredo3
Posté le 15-10-2015 à 11:46:02  profilanswer
 

La méthode "CreateCategory" en ligne 3, crée justement l'objet en base de donnée.
Avant cette ligne son id est bien évidemment 0, après le CreateCategory l'id est bien incrémenté.

 

Mais même sans créer l'objet, par exemple en utilisant à la place la ligne 5 en commentaire "catService.GetCategory(1);", un objet existant en db est chargé au lieu de le créer, pourtant le problème est le même.

Message cité 1 fois
Message édité par fredo3 le 15-10-2015 à 11:46:56
n°2267762
TotalRecal​l
Posté le 15-10-2015 à 12:25:04  profilanswer
 

fredo3 a écrit :


Mais même sans créer l'objet, par exemple en utilisant à la place la ligne 5 en commentaire "catService.GetCategory(1);", un objet existant en db est chargé au lieu de le créer, pourtant le problème est le même.


C'est pas clair pour moi :pt1cable:
T'es en train de me dire qu'en liant cette instance d'objet chargé depuis la base à ton contact tu te retrouves avec une nouvelle catégorie ? Et avec un id différent dans ce cas ? :heink:
pas possible :pt1cable:


Message édité par TotalRecall le 15-10-2015 à 12:25:44

---------------
Topic .Net - C# @ Prog
mood
Publicité
Posté le 15-10-2015 à 12:25:04  profilanswer
 

n°2267764
fredo3
Posté le 15-10-2015 à 12:27:40  profilanswer
 

Après sauvegarde du contact, oui c'est ça :D

 

En gros j'associe au contact la catégorie qui a un ID = 1. Je sauvegarde le contact, et il se retrouve avec une catégorie avec ID = 2 :D.


Message édité par fredo3 le 15-10-2015 à 12:28:16
n°2267768
TotalRecal​l
Posté le 15-10-2015 à 14:11:21  profilanswer
 

Poste le code de la version qui est sensé faire ça avec le GetCategory(1).


---------------
Topic .Net - C# @ Prog
n°2268468
drasche
Posté le 26-10-2015 à 16:49:20  profilanswer
 

J'ai pas tout compris non plus.
 
Je soupçonne que tu essaies d'assigner un objet Type à ton Contact au lieu d'assigner uniquement l'ID. (j'ai plus fait d'EF depuis 11 mois mais il me semble que j'avais fait une erreur similaire au tout début :D )
 
Genre:
 

Code :
  1. Contact.Type = Type;


 
au lieu de  
 

Code :
  1. Contact.TypeId = Type.TypeId;



---------------
Whichever format the fan may want to listen is fine with us – vinyl, wax cylinders, shellac, 8-track, iPod, cloud storage, cranial implants – just as long as it’s loud and rockin' (Billy Gibbons, ZZ Top)

Aller à :
Ajouter une réponse
  FORUM HardWare.fr
  Programmation
  C#/.NET managed

  Entity Framework - Doublon dans la DB

 

Sujets relatifs
Connexion a SQL via Entity FrameworkDelete doublon
Lecture Flux XML, Doublon et ressources SQLRéalisation d'un framework de log java
utilisation du framework Spring MVC[Mysql] DB Mediawiki ne contient plus que hitcounter et searchindex
Problème mise à jour des entity JPA /glassfishChercher / Copier / coller / renomer les faux doublon
Problème framework slim sur serveur dédier 
Plus de sujets relatifs à : Entity Framework - Doublon dans la DB


Copyright © 1997-2022 Hardware.fr SARL (Signaler un contenu illicite / Données personnelles) / Groupe LDLC / Shop HFR