实体框架将持久对象附加到新对象

时间:2009-12-14 21:44:21

标签: entity-framework entity

我正在尝试执行一个非常简单的任务,即“在数据库中添加具有角色的用户”。角色已经填充在数据库中,我只是将角色添加到用户角色集合中,但它不断抛出以下异常:

只有当属性的当前值为null时,才能设置EntityKey属性。

以下是User.cs中的代码:

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

以下测试失败了:

  [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);             
        }

存储库代码:

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

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

            return isSaved; 

        }

这是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);                 
            }
        }

这是完整的例外:

------ 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).

更新:

这是我的UserRepository和RoleRepository,它们都使用不同的上下文:

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

因为,您可以看到用户和角色正在使用您已经指出的不同上下文。使用单个datacontext的问题是我无法正确分层应用程序。

1 个答案:

答案 0 :(得分:4)

根据更新的问题再次更新

当您在存储库之间共享上下文时,我不同意您“无法正确分层应用程序”。这是一个你需要解决的问题,但它肯定是可以解决的。此外,我认为您会发现它比您尝试使用多个上下文时创建的问题数量要容易得多。

无论如何,您的问题实际上只有两种可能的解决方案:

  1. 手动跟踪特定实体所附加的上下文,并在必要时将其传输(使用“附加”和“分离”)。
  2. 在存储库实例之间共享上下文。
  3. 在我们的ASP.NET MVC应用程序中,逻辑工作单元是单个Request。因此,我们在请求开始时实例化ObjectContext,在请求结束时将其公开,并在创建它们时将其注入新的存储库。存储库实例永远不会超过单个请求。

    根据更新的问题进行更新

    角色存储库和用户存储库是否都有(单独的)上下文?这是堆栈跟踪中发生的事情:

    1. 您将用户添加到上下文。
    2. RelationshipManager通过用户并确保任何相关实体也在上下文中。除其他事项外,这涉及设置其EntityKey属性。
    3. 假设角色来自不同的上下文(似乎是这种情况,因为否则上下文应该检测到角色已经在上下文中),您应该看到一个错误,指示您无法添加附加到的实体一个上下文到另一个上下文出于某种原因,你在这里没有看到。但是,这不是一个有效的操作。
    4. 而是在分配角色的EntityKey时收到错误。
    5. 在我看来,一次使用一个ObjectContext应该是使用EntityFramework的一般规则。你应该在你绝对被迫的时候使用多个上下文,根据我的经验,这几乎从不。同时使用多个ObjectContexts 显着比一次使用一个更难

      好的,我不知道您的映射的详细信息,但我希望AddRole更像是以下内容:

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

      ...如果用户 - >角色是 .. 或:

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

      如果用户 - >角色是* .. 1。

      如果这没有帮助,请发布异常的堆栈跟踪。