Search code examples
asp.net-mvc-3entity-frameworkhttpcontext

Entity Framework 4.1 with Many-To-Many Update Yields Error Of "An entity object cannot be referenced by multiple instances of IEntityChangeTracker"


My domain model uses EntityFramework 4.1 (and I'm using code-first) to update my DB with linked entities. I have a Member table and then a many-to-many MemberPositions table. The problem is that when I attempt to perform an update on a member with changed permissions, the code throws a run-time error of "An entity object cannot be referenced by multiple instances of IEntityChangeTracker" when I set the following from my repository class:

_context.Entry(entity).State = EntityState.Modified

Now I have read the posts at the following links, but they look outdated and some of the code doesn't work in EF 4.1 / MVC3:

So my questions are:

  1. What do I need to do to get this code working for EF 4.1 and MVC 3 (e.g. HttpContext.Current["myDBEntities"] can't be indexed using today's framework)
  2. More importantly, where would this code need to reside? I can't seem to figure out where to put the "datacontext per user per request" method or class as described in the first link. It doesn't make sense in my domain project, because it does't have access to the the HttpContext, so if it's in my web project, how should I pass it to the domain?

To help you, below is a stripped down version of my domain model:

public class Entity
{
  public int Id { get; set; }
}

public class Member : Entity
{
  public string Name { get; set; }
  public virtual List<MemberPosition> Positions { get; set; }
}

public class MemberPosition : Entity
{
  public string Name { get; set; }
}

public class EfDbContext : DbContext
{
  public DbSet<Member> Members { get; set; }
  public DbSet<MemberPosition> MemberPositions { get; set; }
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    modelBuilder.Configurations.Add(new MemberMap());
    modelBuilder.Configurations.Add(new MemberPositionMap());
  }
}

public class MemberMap : EntityTypeConfiguration<Member>
{
  public MemberMap()
  {
    ToTable("Members");
    HasMany(m => m.Positions).WithMany().Map(
      m => m.ToTable("Member_MemberPositions").MapLeftKey("MemberId").MapRightKey("PositionId"));
  }
}

public class MemberPositionMap : EntityTypeConfiguration<MemberPosition>
{
  public MemberPositionMap()
  {
    ToTable("MemberPositions");
    Property(x => x.Name).IsRequired().HasMaxLength(100);
  }
}

public interface IRepository<TEntity>
{
  bool Update(TEntity entity);
}

public class Repository<TEntity> : IRepository<TEntity> where TEntity : Entity
{
  private readonly EfDbContext _context;
  private readonly DbSet<TEntity> _dbSet;

  public Repository(EfDbContext context)
  {
    _context = context;
    _dbSet = _context.Set<TEntity>();
  }

  public bool Update(TEntity entity)
  {
    _context.Entry(entity).State = EntityState.Modified;
    _context.SaveChanges();
    return true;
  }
}

And, finally, a stripped down version of the Edit method in my MemberController:

public class MemberController : Controller
{
  [HttpPost]
  public ActionResult Edit(MemberDetailViewModel memberDetailViewModel)
  {
    if (ModelState.IsValid)
    {
      var updatedMember = // Gets the member data from the view model...
      var memberRepository = // Creates a Repository<Member>;
      memberRepository.Update(updatedMember);
      return // blah... blah... blah...
    }
  }
}

Any suggestions are appreciated!

EDIT: THIS IS THE SOLUTION (IT'S EASY!)

In the App_Start.NinjectMVC3 controller, use the following code (make sure that InRequestScope) is present. In my original code, it wasn't, hence it wasn't working...

private static void RegisterServices(IKernel kernel)
{
  kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>)).InRequestScope();
}

Solution

  • As Slauma stated (and as included in my original post as an edit), here's the solution.

    In the App_Start.NinjectMVC3 controller, use the following code (make sure that InRequestScope) is present. In my original code, it wasn't, hence it wasn't working...

    private static void RegisterServices(IKernel kernel)
    {
      kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>)).InRequestScope();
    }