Entity Framework Attacher un objet persistant au nouvel objet

entity entity-framework

Question

J'essaie d'exécuter une tâche très simple qui consiste à "Ajouter l'utilisateur ayant un rôle dans la base de données". Les rôles sont déjà renseignés dans la base de données et je ne fais qu'ajouter le rôle à la collection de rôles d'utilisateur, mais cette exception continue de générer l'exception suivante:

La propriété EntityKey ne peut être définie que lorsque la valeur actuelle de la propriété est null.

Voici le code dans User.cs:

  public void AddRole(Role role)
        {
            if (!Exists(role))
            {
                 role.User = this;
                Roles.Add(role);                 
            }
        }

Et voici le test qui échoue:

  [Test]        
        public void should_save_user_with_role_successfully() 
        {
            var _role = _roleRepository.GetByName("Student");                        

            _user.AddRole(_role); 

            _userRepository.Save(_user);

            Assert.IsTrue(_user.UserId > 0);             
        }

Le code du référentiel:

  public bool Save(User user)
        {
            bool isSaved = false; 

            using (var db = new EStudyDevDatabaseEntities())
            {   
                db.AddToUsers(user);
                isSaved = db.SaveChanges() > 0;  
            }

            return isSaved; 

        }

Voici la méthode AddRole:

   public bool Exists(Role role)
        {
            var assignedRole = (from r in Roles
                                where r.RoleName.Equals(role.RoleName)
                                select r).SingleOrDefault();

            if (assignedRole != null) return true;

            return false; 
        }

        public void AddRole(Role role)
        {
            if (!Exists(role))
            {
                 role.User = this;
                Roles.Add(role);                 
            }
        }

Et voici toute l'exception:

------ Test started: Assembly: EStudy.Repositories.TestSuite.dll ------

TestCase 'EStudy.Repositories.TestSuite.Repositories.when_saving_new_user.should_save_user_with_role_successfully'
failed: System.InvalidOperationException : The EntityKey property can only be set when the current value of the property is null.
 at System.Data.Objects.EntityEntry.GetAndValidateChangeMemberInfo(String entityMemberName, Object complexObject, String complexObjectMemberName, StateManagerTypeMetadata& typeMetadata, String& changingMemberName, Object& changingObject)
 at System.Data.Objects.EntityEntry.EntityMemberChanging(String entityMemberName, Object complexObject, String complexObjectMemberName)
 at System.Data.Objects.EntityEntry.EntityMemberChanging(String entityMemberName)
 at System.Data.Objects.ObjectStateEntry.System.Data.Objects.DataClasses.IEntityChangeTracker.EntityMemberChanging(String entityMemberName)
 at System.Data.Objects.DataClasses.EntityObject.set_EntityKey(EntityKey value)
 at System.Data.Objects.Internal.LightweightEntityWrapper`1.set_EntityKey(EntityKey value)
 at System.Data.Objects.ObjectStateManager.AddEntry(IEntityWrapper wrappedObject, EntityKey passedKey, EntitySet entitySet, String argumentName, Boolean isAdded)
 at System.Data.Objects.ObjectContext.AddSingleObject(EntitySet entitySet, IEntityWrapper wrappedEntity, String argumentName)
 at System.Data.Objects.DataClasses.RelatedEnd.AddEntityToObjectStateManager(IEntityWrapper wrappedEntity, Boolean doAttach)
 at System.Data.Objects.DataClasses.RelatedEnd.AddGraphToObjectStateManager(IEntityWrapper wrappedEntity, Boolean relationshipAlreadyExists, Boolean addRelationshipAsUnchanged, Boolean doAttach)
 at System.Data.Objects.DataClasses.RelatedEnd.IncludeEntity(IEntityWrapper wrappedEntity, Boolean addRelationshipAsUnchanged, Boolean doAttach)
 at System.Data.Objects.DataClasses.EntityCollection`1.Include(Boolean addRelationshipAsUnchanged, Boolean doAttach)
 at System.Data.Objects.DataClasses.RelationshipManager.AddRelatedEntitiesToObjectStateManager(Boolean doAttach)
 at System.Data.Objects.ObjectContext.AddObject(String entitySetName, Object entity)
 C:\Projects\EStudy\EStudySolution\EStudy.BusinessObjects\Entities\EStudyModel.Designer.cs(97,0): at EStudy.BusinessObjects.Entities.EStudyDevDatabaseEntities.AddToUsers(User user)
 C:\Projects\EStudy\EStudySolution\EStudy.BusinessObjects\Repositories\UserRepository.cs(17,0): at EStudy.BusinessObjects.Repositories.UserRepository.Save(User user)
 C:\Projects\EStudy\EStudySolution\EStudy.Repositories.TestSuite\Repositories\Test_UserRepository.cs(47,0): at EStudy.Repositories.TestSuite.Repositories.when_saving_new_user.should_save_user_with_role_successfully()


0 passed, 1 failed, 0 skipped, took 6.07 seconds (NUnit 2.5).

METTRE À JOUR:

Voici mon UserRepository et RoleRepository et ils utilisent tous deux des contextes distincts:

  public bool Save(User user)
        {
            bool isSaved = false; 

            using (var db = new EStudyDevDatabaseEntities())
            {              
                db.AddToUsers(user);
                isSaved = db.SaveChanges() > 0;  
            }

            return isSaved; 

        }

 public Role GetByName(string roleName)
        {
            using (var db = new EStudyDevDatabaseEntities())
            {
                return db.Roles.SingleOrDefault(x => x.RoleName.ToLower().Equals(roleName.ToLower())); 
            }           
        }

En tant que, vous pouvez voir que l'utilisateur et le rôle utilisent un contexte différent que vous avez déjà souligné. Le problème lié à l'utilisation d'un seul contexte de données est que je ne peux pas superposer correctement l'application.

Réponse acceptée

Mis à jour à nouveau en fonction de la question mise à jour

Je ne suis pas d'accord pour dire que vous "ne pouvez pas superposer correctement l'application" lorsque vous partagez un contexte entre des référentiels. C'est un problème que vous devez résoudre, mais qui peut certainement être résolu. De plus, je pense que vous trouverez qu'il est beaucoup plus facile à résoudre que le nombre de problèmes que vous créez lorsque vous essayez d'utiliser plusieurs contextes.

Quoi qu'il en soit, il n'y a en réalité que deux solutions possibles à votre problème:

  1. Maintenez manuellement le suivi du contexte auquel une entité particulière est attachée et transférez-le (avec Attacher et Détacher), si nécessaire.
  2. Partager un contexte entre les instances de référentiel.

Dans nos applications ASP.NET MVC, l'unité logique de travail est une requête unique. Par conséquent, nous instancions un ObjectContext au début d'une requête, nous le supprimons à la fin d'une requête et l'injectons dans de nouveaux référentiels lorsque nous les créons. Les instances de référentiel ne survivent jamais à une seule requête.

Mise à jour basée sur la question mise à jour

Le référentiel de rôles et le référentiel d'utilisateurs ont-ils chacun un contexte (séparé)? Voici ce qui se passe dans la trace de la pile:

  1. Vous ajoutez l'utilisateur au contexte.
  2. RelationshipManager passe par l'utilisateur et s'assure que toutes les entités associées sont également dans le contexte. Cela implique, entre autres, la définition de leur propriété EntityKey.
  3. En supposant que le rôle vienne d'un contexte différent (ce qui semble être le cas, car sinon le contexte devrait détecter que le rôle est déjà dans le contexte), vous devriez voir une erreur indiquant que vous ne pouvez pas ajouter une entité associée à un contexte un autre contexte. Pour une raison quelconque, vous ne voyez pas cela ici. Mais néanmoins, ce n'est pas une opération valide.
  4. Au lieu de cela, vous obtenez une erreur lorsque la clé EntityKey du rôle est attribuée.

À mon avis, utiliser un seul ObjectContext à la fois devrait être la règle générale pour utiliser EntityFramework. Vous ne devez utiliser plusieurs contextes que lorsque vous y êtes absolument obligé, ce qui, selon mon expérience, n’est presque jamais. Travailler avec plusieurs ObjectContexts simultanément est beaucoup plus difficile que de travailler avec un à la fois.

OK, je ne connais pas les détails de votre mappage, mais je m'attendrais à ce que AddRole soit un peu plus AddRole à:

public void AddRole(Role role)
{
    this.Roles.Add(role);
}

... si User-> Role is .. ou:

public void AddRole(Role role)
{
    this.Role = role;
}

si Utilisateur -> Le rôle est * .. 1.

Si cela ne vous aide pas, envoyez la trace de pile pour l'exception.



Related

Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow