Entity Framework Присоединение сохраненного объекта к новому объекту - PullRequest
2 голосов
/ 15 декабря 2009

Я пытаюсь выполнить очень простую задачу: «Добавить пользователя с ролью в базе данных». Роли уже заполнены в базе данных, и я просто добавляю роль в коллекцию ролей пользователей, но она продолжает выдавать следующее исключение:

Свойство 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 Ответ

4 голосов
/ 15 декабря 2009

Обновлено снова на основе обновленного вопроса

Я не согласен с тем, что вы "не можете правильно наложить приложение", когда делитесь контекстом между репозиториями. Это проблема, которую вам нужно решить, но она наверняка решаема. Кроме того, я думаю, что вам будет гораздо проще решать, чем количество проблем, которые вы создаете, когда пытаетесь использовать несколько контекстов.

В любом случае, существует только два возможных решения вашей проблемы:

  1. Вручную отследите, к какому контексту привязан конкретный объект, и перенесите его (с помощью Attach и Detach), когда это необходимо.
  2. Совместное использование контекста между экземплярами репозитория.

В наших приложениях ASP.NET MVC логической единицей работы является один запрос. Поэтому мы создаем экземпляр ObjectContext в начале запроса, удаляем его в конце запроса и внедряем в новые репозитории при их создании. Экземпляры репозитория никогда не выдерживают ни одного запроса.

Обновление на основе обновленного вопроса

Имеет ли хранилище ролей и хранилище пользователей каждый (отдельный) контекст? Вот что происходит в трассировке стека:

  1. Вы добавляете пользователя в контекст.
  2. RelationshipManager проходит через пользователя и гарантирует, что любые связанные объекты также находятся в контексте. Это включает, помимо прочего, установку их свойства EntityKey.
  3. Предполагая, что Роль произошла из другого контекста (который, как представляется, имеет место, поскольку в противном случае контекст должен обнаружить, что роль уже находится в этом контексте), вы должны увидеть ошибку, указывающую, что вы не можете добавить объект, присоединенный один контекст в другой контекст. По какой-то причине вы не видите этого здесь. Но, тем не менее, это недопустимая операция.
  4. Вместо этого вы получаете сообщение об ошибке, когда EntityKey роли назначен.

По моему мнению, использование одного ObjectContext за раз должно быть общим правилом для работы с EntityFramework. Вы должны использовать несколько контекстов только тогда, когда вы абсолютно вынуждены, что, по моему опыту, почти никогда. Одновременная работа с несколькими ObjectContexts на значительно сложнее, чем работа с одним за раз.

ОК, я не знаю деталей вашего отображения, но я бы ожидал, что AddRole будет чем-то более похожим на:

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

... если пользователь-> Роль .. или:

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

если Пользователь -> Роль * .. 1.

Если это не поможет, опубликуйте трассировку стека для исключения.

...