Insert one record into many records

I am having a problem with inserts in Entity Framework.

My application has the following two objects:

  • Reminder
  • Employee

A note can be associated with multiple employees.
An employee can be associated with multiple memos.

This means many to many. I have read several articles explaining to me that a join table should be created, which I think is obvious.

In the articles, I learned that Entity Framework will automatically create a join table for me. So I did it like this:

Reminder

public Guid MemoId { get; set; }
public String Message { get; set; }
public virtual ICollection<Employee> Employees { get; set; }

      

Employee

public Guid EmployeeId { get; set; }
public String Name { get; set; }
public virtual ICollection<Memo> Memos { get; set; }

      

When updating my database using the Package Manager Console, a connection table was created in the database. I did it using the following line:
update-database -force -verbose

I have an idea about creating a new note. Here you can select a list of employees and add them to the memo. However, populating this junction table does not proceed as planned. I think it has something to do with setting up my repositories. I created MemoRepository and EmployeeRepository.

My controller managing Memo creation looks like this:

MemoController

public class MemoController : Controller
{
    private IMemoRepository _memoRepository;
    private IEmployeeRepository _employeeRepository;

    public MemoController(IMemoRepository memoRepository, IEmployeeRepository employeeRepository) {
        _memoRepository = memoRepository;
        _employeeRepository = employeeRepository;
    }

    public ViewResult Create() {
        //Initializes MemoCreateViewModel here
        return View(model);
    }

    [HttpPost]
    public ActionResult Create(MemoCreateViewModel model) {
        if(!ModelState.IsValid)
            return RedirectToAction("Create");

        Guid employeeId;
        List<Guid> employeeIds = new List<Guid>();
        foreach (var id in model.SelectedEmployeeIds) {
            if (!Guid.TryParse(id, out employeeId)) {
                continue;
            }
            employeeIds.Add(employeeId);
        }
        var employees = _employeeRepository.GetEmployeesByIds(employeeIds);
        model.Memo.Employees = employees.ToList<Employee>();
        _memoRepository.SaveMemo(model.Memo);

        return RedirectToAction("List");
    }
}

      

MemoRepository

public class EFMemoRepository : IMemoRepository
{
    private EFDbContext context;

    public EFMemoRepository(EFDbContext _context) {
        context = _context;
    }

    public IQueriable<Memo> Memos {
        return context.Memos;
    }

    public void SaveMemo(Memo memo) {
        if(memo.MemoId == Guid.Empty) {
            memo.MemoId = Guid.NewGuid();
            context.Memos.Add(memo); //error 1 here 
        } else {
            Memo dbEntry = context.Memos.Find(memo.MemoId);
            if(dbEntry != null) {
                dbEntry.Message = memo.Message;
                dbEntry.Employees = memo.Employees;
            }
        }
    context.SaveChanges(); //error 2 here
    }
}

      

Error 1 I get on insert:

An entity object cannot reference multiple instances of IEntityChangeTracker.

Error 2 While updating:

The relationship between the two objects cannot be determined because they are bound to different ObjectContext objects.

How can I solve this, I have read threads about people saying it has to do with using different contexts, others say it has something to do with Attach()

, but I don't know how to resolve this in my application.

Please tell me if you need more information.

Note. I left the code behind to make it easier to read. Code can be added if required of course.

+3


source to share


1 answer


You get the first error because you are adding a Memo object that is being passed to the context. But the employee object that was added to the memo object inside the controller was retrieved using a different dbContext. To fix this, you must share the db context between the two OR you must explicitly bind the Employee object to the current context.

Option 1: CONTROLLER CODE

[HttpPost]
public ActionResult Create(MemoCreateViewModel model) {
    if(!ModelState.IsValid)
        return RedirectToAction("Create");

    Guid employeeId;
    List<Guid> employeeIds = new List<Guid>();
    foreach (var id in model.SelectedEmployeeIds) {
        if (!Guid.TryParse(id, out employeeId)) {
            continue;
        }
        employeeIds.Add(employeeId);
    }
    EFDbContext dbContext = new EFDbContext();//Note
    var employees = _employeeRepository.GetEmployeesByIds(dbContext, employeeIds);//Note the extra parameter
    model.Memo.Employees = employees.ToList<Employee>();
    _memoRepository.SaveMemo(dbContext,model.Memo);//Note the extra parameter

    return RedirectToAction("List");
}

      

EFMemoRepository CLASS CLASS:

public void SaveMemo(EFDbContext dbContext, Memo memo) 
{
    if(memo.MemoId == Guid.Empty) 
    {
        memo.MemoId = Guid.NewGuid();
        context.Memos.Add(memo); //error 1 here 
    } else 
    {
        Memo dbEntry = dbContext.Memos.Find(memo.MemoId);
        if(dbEntry != null) 
        {
            dbEntry.Message = memo.Message;
            for (int i = 0; i < dbEntry.Employees.Count; i++)/*Please note that if lazy loading is not True then this reference must explicitly be loaded*/
            {
              dbEntry.Employees.Remove(dbEntry.Employees.First());
            }
            foreach (var item in memo.Employees)
            {
                dbEntry.Employees.Add(item);
            }
           context.Entry(dbEntry).State = EntityState.Modified;                
        }
    }
    dbContext.SaveChanges(); //error 2 here
}

      



OR

OPTION 2:

public void SaveMemo(Memo memo) 
{
    if(memo.MemoId == Guid.Empty) 
    {
        memo.MemoId = Guid.NewGuid();
        context.Memos.Add(memo); //error 1 here 
    } else 
    {
        Memo dbEntry = context.Memos.Find(memo.MemoId);
        if(dbEntry != null) 
        {
            dbEntry.Message = memo.Message;
            for (int i = 0; i < dbEntry.Employees.Count; i++)/*Please note that if lazy loading is not True then this reference must explicitly be loaded*/
            {
               dbEntry.Employees.Remove(dbEntry.Employees.First());
            }
            foreach (var item in memo.Employees)
            {
                dbEntry.Employees.Add(item);
            }   
            context.Entry(dbEntry).State = EntityState.Modified;              
        }
    }
    context.SaveChanges(); //error 2 here
}

      

Personally, I would go with option 1 or something along those lines.

Scream if something is unclear

+1


source







All Articles