Shawn Mclean
Shawn Mclean

Reputation: 57469

Error in detaching and attaching objects in entity framework

I get the object, using NoTracking, convert and manipulate it, send it to an asp.net mvc view. I get back an edited version, call the edit method in my repository, try to Attach it, it throws the following error:

An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.

How I get the data:

IQueryable<User> query = db.Users.AsNoTracking();

How I edit the data:

    public User UpdateUser(User user, IEnumerable<Expression<Func<User, object>>> properties)
    {
            db.Users.Attach(user); //Error happens right here.
            foreach (var selector in properties)
            {
                string propertyName = Helpers.PropertyToString(selector.Body);
                db.Entry(user).Property(propertyName).IsModified = true;
            }
            db.SaveChanges();
            return user;
    }

When I get the entities using AsNoTracking, shouldn't that detach the thing for me? Even if it didn't detach, the whole asp.net life cycle is supposed to be restarted on resubmitting the data, which would make the graph empty.

What am I doing wrong here?

I do my bindings like this:

DbContext db = new DbContext("ConnStringName");
Bind<IUserRepository>().To<SqlServerUserRepository>()
                .WithConstructorArgument("db", db);

Upvotes: 2

Views: 768

Answers (1)

Ed Chapel
Ed Chapel

Reputation: 6932

Sounds like your db context is created once and that instance is effectively a singleton. When you attempt to attach the User, the context detects that it already has one in the cache and fails.

I recommend changing the context to be created per request. With Ninject, you can bind it with

Bind<YourDbContext>().ToMethod(context => CreateContext()).InRequestScope();

where YourDbContext is the type of the db variable and CreateContext() is the method in your global.ascx.

UPDATE

You are indeed creating your database context once and storing it as a singleton. No worries, you can set up your bindings as

// DbContext db = new DbContext("ConnStringName");
Bind<DbContext>().ToMethod(context => new DbContext("ConnStringName")).InRequestScope();
Bind<IUserRepository>().To<SqlServerUserRepository>().InRequestScope();

This will create a new DbCOntext for each Request and prevent the cached User from colliding with the submitted User when attaching.

Upvotes: 1

Related Questions