0

I'm trying to use the repository pattern with EntityFramework in an MVC application to update a record, but the db.SaveChanges() method isn't saving to the database. I'm not getting any errors, and I can step through the code and see the correct variables getting passed, as well as the record variable, getting updated.

Based on what I've been reading, I believe my problem is that I have two instance of my database context (MyDBEntities). The problem might be that my ReadRepository, which creates an instance of MyDBEntities is injected into the WriteRepository which also creates an instance MyDBEntities. I'm not really sure how to inject the entities into the repositories (assuming that's what I need to do).

If I try to add something like db.MyTable.Attach(record); before db.SaveChanges(), I'll get this error below, which makes me think I need to inject MyDBEntities().

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

CONTROLLER

public class MyController : Controller
{
    private IWriteRepository _writeRepository;

    public MyController(IWriteRepository writeRepository)
    {
        _writeRepository = writeRepository;
    }

    [HttpPost]
    public ActionResult MyPostMethod(MyViewModel model)
    {
        try
        {
            //Send to the repository to edit a record
            writeRepository.EditRecord(model);

            return new HttpStatusCodeResult(HttpStatusCode.OK);
        }
        catch
        {
            throw new HttpException(500, "Internal Server Error");
        }            
    }
}

WRITE REPOSITORY

//The ReadRepository is injected into the WriteRepository to grab records from the database
public class WriteRepository : IWriteRepository
{
    private MyDBEntities db = new MyDBEntities();
    private IReadRepository _readRepository;

    public WriteRepository(IReadRepository readeadRepository)
    {
        _readRepository = readRepository;
    }        

    public void EditRecord(MyViewModel model)
    {
        //Get the specific record to be updated
        var record = readRepository.GetRecord(model.ID);

        //Update the data
        record.FirstName = model.Name;

        //This isn't saving
        db.SaveChanges();
    }
}

READ REPOSITORY

public class ReadRepository : IReadRepository
{
    private MyDBEntities db = new MyDBEntities();

    //Provides the record to the write repository to edit
    public MyTable GetRecord(int ID)
    {
        var record = (from t in db.MyTable
                      where t.ID == ID
                      select t).SingleOrDefault();

        return record;
    }
}
madvora
  • 1,717
  • 7
  • 34
  • 49
  • Possible duplicate of [entity object cannot be referenced by multiple instances of IEntityChangeTracker. while adding related objects to entity in Entity Framework 4.1](http://stackoverflow.com/questions/10191734/entity-object-cannot-be-referenced-by-multiple-instances-of-ientitychangetracker) – James P May 22 '16 at 00:26
  • I did read that one, but the part I'm looking for help with its how to create the database context outside of the repositories. – madvora May 22 '16 at 00:50

1 Answers1

5

From your MyController code it looks like you're already using an Inversion of Control framework, so update your ReadRepository and WriteRepository classes as follows:

ReadRepository

public class ReadRepository : IReadRepository
{
    private MyDBEntities db;

    public ReadRepository(MyDBEntities myDbEntities) 
    {
        db = myDbEntities;
    }

    //Provides the record to the write repository to edit
    public MyTable GetRecord(int ID)
    {
        var record = (from t in db.MyTable
                      where t.ID == ID
                      select t).SingleOrDefault();

        return record;
    }
}

WriteRepository

public class WriteRepository : IWriteRepository
{
    private MyDBEntities db;
    private IReadRepository _readRepository;

    public WriteRepository(MyDBEntities myDbEntities, IReadRepository readeadRepository)
    {
        db = myDbEntities;
        _readRepository = readRepository;
    }        

    public void EditRecord(MyViewModel model)
    {
        //Get the specific record to be updated
        var record = readRepository.GetRecord(model.ID);

        //Update the data
        record.FirstName = model.Name;

        //This isn't saving
        db.SaveChanges();
    }
}

Both your ReadRepository and WriteRepository depend on an instance of MyDBEntities to do work, so you should specify that dependency in the constructor of those classes and let your Inversion of Control framework handle creating it rather than creating a new instance of MyDBEntities yourself.

Make sure that in the registrations for your Inversion of Control framework you're registering your MyDBEntities instance as scoped, or per request, and not transient. You want to ensure that on each request both your ReadRepository and WriteRepository are getting injected with the same instance of your MyDBEntities class rather than two separate instances as they are now.

Update your Unity configuration as follows:

UnityConfig.cs

Register your MyDBEntities class with Unity in a per-request scope.

public class UnityConfig
{
    // ... other methods ...

    public static void RegisterTypes(IUnityContainer container)
    {
        // ... other registrations ...
        container.RegisterType<IReadRepository, ReadRepository>();
        container.RegisterType<IWriteRepository, WriteRepository>();
        container.RegisterType<MyDBEntities>(new PerRequestLifetimeManager());
        // ... other registrations ...
    }
}

UnityMvcActivator.cs

Enable the per-request scope in Unity by uncommenting the line under the TODO.

public static class UnityWebActivator
{
    public static void Start()
    {
        // ... start code ...
        // TODO: Uncomment if you want to use PerRequestLifetimeManager
        Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
    }

    // ... other methods ...
}
Corey Smith
  • 1,643
  • 1
  • 14
  • 21
  • Outstanding answer. You're right on track with what I need. I'm actually using Unity for the framework, but can't figure out what to register or how to register the entities for the last part. – madvora May 22 '16 at 12:06
  • Okay I updated my answer with registrations for Unity. – Corey Smith May 22 '16 at 21:02
  • Thanks for the update. I'll try this out and mark this as the answer if everything works. – madvora May 23 '16 at 01:33
  • Can we get some feedback, @madvora? Please mark as answered or add additional follow up comments. Zombie questions hurt everyone. – Corey Smith Jun 06 '16 at 17:44