2 votes

Attacher un objet persistant Entity Framework à un nouvel objet

Je tente d'effectuer une tâche très simple qui est "Ajouter l'utilisateur avec un rôle dans la base de données". Les rôles sont déjà peuplés dans la base de données et j'ajoute simplement le rôle à la collection des rôles de l'utilisateur mais cela continue de jeter l'exception suivante :

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

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 devrait_enregistrer_l_utilisateur_avec_succès() 
        {
            var _role = _roleRepository.GetByName("Étudiant");                        

            _user.AddRole(_role); 

            _userRepository.Save(_user);

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

Le Code du Repository:

  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 démarré : 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).

MISE À 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())); 
            }           
        }

Comme vous pouvez le voir, l'utilisateur et le rôle utilisent des contextes différents comme vous l'avez déjà souligné. Le problème avec l'utilisation d'un seul DataContext est que je ne peux pas structurer correctement l'application.

4voto

Craig Stuntz Points 95965

Mise à jour encore une fois basée sur la question mise à jour

Je ne suis pas d'accord avec le fait que vous ne "pouvez pas superposer correctement l'application" lorsque vous partagez un contexte entre les dépôts. C'est un problème que vous devez résoudre, mais il est certainement résoluble. De plus, je pense que vous trouverez considérablement plus facile à résoudre que le nombre de problèmes que vous créez lorsque vous essayez d'utiliser plusieurs contextes.

En tout cas, il n'existe vraiment que deux solutions possibles à votre problème:

  1. Suivre manuellement le contexte auquel une entité particulière est attachée, et la transférer (avec Attacher et Détacher), si nécessaire.
  2. Partager un contexte entre les instances de dépôt.

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

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

Est-ce que le dépôt de rôles et le dépôt d'utilisateurs ont chacun un contexte (séparé) ? Voici ce qui se passe dans la trace de la pile:

  1. Vous ajoutez l'Utilisateur au contexte.
  2. Le RelationshipManager passe par l'Utilisateur et s'assure que toutes les entités liées sont également dans le contexte. Cela implique, entre autres choses, la définition de leur propriété EntityKey.
  3. En supposant que le Rôle provienne 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é attachée à un contexte dans 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. À la place, vous obtenez une erreur lorsque la EntityKey du rôle est attribuée.

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

D'accord, je ne connais pas les détails de votre mappage, mais je m'attendrais à ce que AjouterRole soit quelque chose de plus dans cette veine:

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

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

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

si User -> Role est *..1.

Si cela ne vous aide pas, veuillez poster la trace de la pile de l'exception.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X