none
Stucked by EF4 - An entity object cannot be referenced by multiple instances of IEntityChangeTracker. Please? RRS feed

  • Question

  • Hello,

    I have created a few repositories and services with EF4 but I am having huge problems.

    I am creating a User as follows:

    UserService service = new UserService();
    Role administrator = new Role { Id = 1 };
    Role collaborator = new Role { Id = 2 };
    User user = new User {
     Email = "john@xyz.xyz",
     Username = "john"
    };
    user.Roles.Add(president);
    user.Roles.Add(administrator);
    service.Create(user);
    

    And I get the following error:

      An entity object cannot be referenced by multiple instances of IEntityChangeTracker.

    I tried everything I could think of to get rid of it but I can't find the problem.

    I created an ADO Entity Data Model name Context. Context generate class is a partial class so I created the following:

     public interface IContext {
      void Save();
      void Save(SaveOptions options);
      void Free();
     } // IContext
    
     public partial class Context : ObjectContext, IContext {
    
      public void Save() {
       base.SaveChanges();
      } // Save
    
      public void Save(SaveOptions options) {
       base.SaveChanges(options);
      } // Save
    
      public void Free() {
       base.Dispose();
      } // Free
    
     } // Context
    

    Then I created my repository (I leave some methods out for sake of simplicity):

     public interface IRepository<T> {
    
      void Add(T entity);
      void Attach(T entity);
      T First(Func<T, Boolean> predicate);
      void Save();
      void Save(SaveOptions options);
      T Single(Func<T, Boolean> predicate);
    
     } // IRepository
    
     public class Repository<T> : IRepository<T> where T : class {
    
      private Context _context;
      private IObjectSet<T> _set;
    
      public Repository()
       : this(new Context()) {
      } // Repository
    
      public Repository(IContext context) {
    
       _context = (Context)context;
       _set = _context.CreateObjectSet<T>();
    
      } // Repository
    
      public void Add(T entity) {
       if (entity == null)
        throw new ArgumentNullException("entity");
       _set.AddObject(entity);
      } // Add
    
      public void Attach(T entity) {
       _set.Attach(entity);
      } // Attach
    
      public T First(Func<T, Boolean> predicate) {
       return _set.First<T>(predicate);
      } // First
    
      public void Free() {
       _context.Free();
      } // Free
    
      public void Save() {
       _context.Save();
      } // Save
    
      public void Save(SaveOptions options) {
       _context.Save(options);
      } // Save
    
     } // Repository
    

    Then, for example, IUserRepository and UserRepository are simply as follows:

     public interface IUserRepository : IRepository<User> {
     } // IUserRepository
    
     public class UserRepository : Repository<User>, IUserRepository {
    
      public UserRepository()
       : this(new Context()) {
      } // UserRepository
    
      public UserRepository(IContext context)
       : base(context) {
      } // UserRepository
    
     } // UserRepository
    

    Finally, my IUserService and UserService I am using on my test is something like:

     public interface IUserService {
      void Create(User user);
     } // IUserService
    
     public class UserService : IUserService {
    
      private RoleRepository _roleRepository;
      private UserRepository _userRepository;
    
      public UserService()
       : this(new RoleRepository(), new UserRepository()) {
      } // UserService
    
      public UserService(IRoleRepository roleRepository, IUserRepository userRepository) {
    
       _roleRepository = (RoleRepository)roleRepository;
       _userRepository = (UserRepository)userRepository;
    
      } // UserService
    
      public void Create(User user) {
    
       foreach (Role role in user.Roles)
        _roleRepository.Attach(role);
    
       _userRepository.Add(user);
       _userRepository.Save();
    
      } // Create
    
     } // UserService
    
    
    

    However, whatever I do I keep being stuck when creating an User.

    Does anyone, has any idea, what might be wrong?

    I have been trying everything I can think off but I am still stucked.

    Thanks,

    Miguel

    Monday, September 6, 2010 2:49 PM

Answers

  • Hi Miguel,

    I have answered you in the other thread (http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/ad659065-1eb7-4f56-8261-ba834e7d5392).

    Has it solved your problem?

    As you have an object graph with some objects "new" and others "already persisted" you need to tell EF wich ones are new and wich ones aren't.

    To do this you can do the following:

    - Obtain the already persisted objects from the database and then use the AddObject method in order to insert the object graph. As EF will find the already persisted objects in the context (you have just requested them), it will recognize that those objects are not new, so they shouldnt be inserted in the database. This is RECOMMENDED option. But, if you want to avoid to request the already persisted objects, you can choose one of the following options.

    - Use the Attach method in order to attach the object graph and then change the state of the new entities to "Added": 

    context.[EntitySet].Attach(parentEntity);
    context.ObjectStateManager.ChangeObjectState(newEntity, System.Data.EntityState.Added); // do this for each new entity
    

     - Use the AddObject method in order add the object graph and then change the state of the already persisted objects to "Unchanged":

    context.[EntitySet].AddObject(parentEntity);
    context.ObjectStateManager.ChangeObjectState(persistedEntity, System.Data.EntityState.Unchanged); // do this for each persisted entity
    

    Please let me know if this solved your issue.

    Regards,

    Jorge

    • Proposed as answer by Jorge Fioranelli Tuesday, September 7, 2010 12:23 PM
    • Marked as answer by liurong luo Monday, September 13, 2010 9:18 AM
    Tuesday, September 7, 2010 12:22 PM

All replies

  • Hello,

    when you call _userRepository.Add(user) it will handle all nested objects. You don't need (you can't) attach each role separately.

    Best regards,
    Ladislav

    Monday, September 6, 2010 4:20 PM
  • No,

    On an earlier version I didin't attach them and I got the following error:

    {" Cannot insert the value NULL into column 'Name',  table 'App.dbo.Roles'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated."}

    The roles already exist on the database.

    So I am adding to the user the two roles given the ID's which I know what they are.

    In fact I have a post about this.

    I am just going around and around and all possible solutions I try give me a new error.

    Open to suggestions ...

    Anyone?

    Thanks,

    Miguel

    Monday, September 6, 2010 10:47 PM
  • Hello,

    I think I was able to identify the problem but not to solve it.

    To keep it simple I used the context directly instead of the repositories and services. So I have the following:

     Context context = new Context();
    
     User user = new User {
     Email = "john@xyz.com",
     Username = "johnusr",
     Profile = new Profile { Birthday = DateTime.UtcNow, City = "Lisbon" },
     };
     
     Role administrator = new Role { Id = 1 };
     Role collaborator = new Role { Id = 2 };
    

    I tried three options after this code:

    1) Didn't attach the roles. Just added them to the User entity:    

     

     user.Roles.Add(administrator);
     user.Roles.Add(collaborator);
     
     context.AddToUsers(user);
     context.SaveChanges();
    

     

        And I got the following error:

        Cannot insert the value NULL into column 'Name', table 'dbo.Roles'; column does not allow nulls. INSERT fails.\r\nThe statement has been terminated."}

    2) Attached the roles BEFORE adding them to the user:

     

     context.AttachTo("Roles", administrator);
     context.AttachTo("Roles", collaborator);
    
     user.Roles.Add(administrator);
     user.Roles.Add(collaborator);
     
     context.AddToUsers(user);
     context.SaveChanges();
    
    This works!

     

    3) Attached the roles AFTER adding them to the user:

     

     user.Roles.Add(administrator);
     user.Roles.Add(collaborator);
    
     context.AttachTo("Roles", administrator);
     context.AttachTo("Roles", collaborator);
     
     context.AddToUsers(user);
     context.SaveChanges();
    

     

        And I got the following error:

        An object with the same key already exists in the ObjectStateManager. The existing object is in the Unchanged state. An object can only be added to the ObjectStateManager again if it is in the added state.

     

    Now let me explain what I am doing:

    I am using ASP.NET MVC. So in a form the user inserts some information: Email, Username, Roles, etc.

    What I get is a View Model with the Email, Username and Roles IDs.

    Then I map the View Model to the Entity ... And I get an entity where the Roles have only the ID's.

    Should I, in the mapping, get the complete roles using the RoleRepository? In my opinion not ... The mapper shouldn't use repositories.

     

    So when I send the user to the service I get a User with Roles as in situation 3.

    It is in UserService that I would like to fill the blanks.

    So what I would like to make work is situation 3.

     

    I have been a lot of trouble when using ASP.NET MVC with EF4.

    Does all this make sense? Does anyone has a solution?

    Thanks,

    Miguel

    Tuesday, September 7, 2010 12:57 AM
  • Hi Miguel,

    I have answered you in the other thread (http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/ad659065-1eb7-4f56-8261-ba834e7d5392).

    Has it solved your problem?

    As you have an object graph with some objects "new" and others "already persisted" you need to tell EF wich ones are new and wich ones aren't.

    To do this you can do the following:

    - Obtain the already persisted objects from the database and then use the AddObject method in order to insert the object graph. As EF will find the already persisted objects in the context (you have just requested them), it will recognize that those objects are not new, so they shouldnt be inserted in the database. This is RECOMMENDED option. But, if you want to avoid to request the already persisted objects, you can choose one of the following options.

    - Use the Attach method in order to attach the object graph and then change the state of the new entities to "Added": 

    context.[EntitySet].Attach(parentEntity);
    context.ObjectStateManager.ChangeObjectState(newEntity, System.Data.EntityState.Added); // do this for each new entity
    

     - Use the AddObject method in order add the object graph and then change the state of the already persisted objects to "Unchanged":

    context.[EntitySet].AddObject(parentEntity);
    context.ObjectStateManager.ChangeObjectState(persistedEntity, System.Data.EntityState.Unchanged); // do this for each persisted entity
    

    Please let me know if this solved your issue.

    Regards,

    Jorge

    • Proposed as answer by Jorge Fioranelli Tuesday, September 7, 2010 12:23 PM
    • Marked as answer by liurong luo Monday, September 13, 2010 9:18 AM
    Tuesday, September 7, 2010 12:22 PM