Answered by:
An entity object cannot be referenced by multiple instances of IEntityChangeTracker with repository + unit of work

Question
-
User-875744750 posted
hello thank you for your help
again i have a probleme with an exception "An entity object cannot be referenced by multiple instances of IEntityChangeTracker" when i try to add expense object to database
i heve used ADO.Net Entity Data Model with Entity Framework 5.0 for use model first
and i have used repository pattern with unit of work here is the code like this tutorial
(i have implemented a generic repository class that implement IGeneric Repository Interface)
here is my unit of work code
public class UnitOfWork : IDisposable { private InvoiceContext context = new SchoolContext(); private GenericRepository<Invoice> _invoiceRepository; private GenericRepository<Expense> _expenseRepository; public GenericRepository<Invoice> InvoiceRepository { get { return _invoiceRepository ?? new GenericRepository<Invoice>(context);; } } public GenericRepository<Expense> ExpenseRepository { get { get { return _expenseRepository ?? new GenericRepository<Expense>(context);; } } } public void Save() { context.SaveChanges(); }
code business rule AddExpense
public void AddExpense(Expense expense, string out message) { OnAdding(expense, out message); unitOfWork.ExpenseRepository.Add(expense); unitOfWork.Save(); } private void OnAdding(Expense expense, string out message) { if (expense.Invoice == null) { throw new BusinessRuleException("you can not insert an expense to invoice that's not existed"); } if (expense.Invoice == State.Closed) { throw new BusinessRuleException("you can not insert an expense to invoice that's closed"); } //the rest of code }
the model classes
public class Invoice { public int Id {get; set;} //.....the rest of properies public virtual List<Expense> Expenses{get; set;} } public class Expense { public int Id{get; set;} //.....the rest of properies public virtual Invoice Invoice{get; set;} }
Sunday, January 6, 2019 9:00 PM
Answers
-
User-893317190 posted
Hi bensam16,
From your code, it seems you are using two bll instance?(invoiceBll and expenseBll)
Do the two bll instance share the same UnitWork?
Maybe the exception is caused by that two bll instances use different unitwork.
Please remove your try catch code and debug your code to see which line causes the problem.
Best regards,
Ackerly Xu
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Wednesday, January 9, 2019 7:39 AM -
User-893317190 posted
Hi bensam16,
It's a pity.They use two different UnitOfWork and so use different dbcontent.
You pass the same unitofwork to InvoiceBill and ExpenseBll.
For example:
public class ExpenseBLL { UnitOfWork unitOfWork{get;set;} // the rest of code public ExpenseBll(UnitOfWork unitofwork){ this.unitofWork = unitofwork } } public class InvoiceBLL { UnitOfWork unitOfWork{get;set;} // the rest of code public InvoiceBLL (UnitOfWork unitofwork){ this.unitofWork = unitofwork } }
And then
UnitOfWork unitofWork = new UnitOfWork(); InvoiceBll invoiceBll = new InvoiceBll(unitofWork); ExpenseBll expenseBll = new ExpenseBll(unitofWork);
Best regards,
Ackerly Xu
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Thursday, January 10, 2019 1:13 AM -
User-893317190 posted
Hi bensam16,
Although your two bll extends the same BllBase, they still use different UnitOfWork.
Because every child class 's object will initialize their own parent class , they share different parent class's object.
You could dubug your code , set a breakpoint on return _unitOfWork ?? new UniteOfWork(); to see how many times it is called.
It will be clear whether they have the same UnitOfWork.
Best regards,
Ackerly Xu
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Thursday, January 10, 2019 9:21 AM -
User-893317190 posted
Hi bensam16,
Maybe you could still use unitofwork in bll.
Below is my thought.
First, define bll which combine Unitofwork in bll. The Unitofwork in bll could pass Unitofwork in dal to a single bll through constructor.
public class DepartmentService { private UnitOfWork UnitOfWork { get; set; } public DepartmentService(UnitOfWork unitOfWork) { this.UnitOfWork = unitOfWork; }
// other methods using UnitOfWork } public class EmployeeService { private UnitOfWork UnitOfWork { get; set; } public EmployeeService(UnitOfWork unitOfWork) { this.UnitOfWork = unitOfWork; } }Then define unitofwork in bll.
public class UnitofWorkService { private UnitOfWork unitOfWork = new UnitOfWork(); private EmployeeService employeeService; private DepartmentService departmentService; public EmployeeService EmployeeService { get { if(this.employeeService == null) { this.employeeService = new EmployeeService(unitOfWork); } return employeeService; } } public DepartmentService DepartmentService { get { if (this.departmentService == null) { this.departmentService = new DepartmentService(unitOfWork); } return DepartmentService; } } }
Then you could use the UnitofworkService in controller instead of Unitofwork.
Best regards,
Ackerly Xu
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Friday, January 11, 2019 1:38 AM
All replies
-
User-893317190 posted
Hi bensam16,
The error occurs when your dbcontent tries to save or update an entity that is traced by another dbcontent, which means you are not using a single dbcontent.
For example, I initialize two dbcontext , use one dbcotext to get an entity , change the entity and use the other dbcontent to save the entity.
// initialize two dbcontext
DepartAndEmp dae1 = new DepartAndEmp(); DepartAndEmp dae2 = new DepartAndEmp(); // use dbcontent one to get a employee , which is traced by dbcontext1 Employee employee = dae1.employees.Find(1);
// change the entity's name employee.name = "mijk"; // use the other dbcontext to update the entity traced by dbcontext1 dae2.Entry(employee).State = System.Data.Entity.EntityState.Modified; // save changes of dbcontent2 dae2.SaveChanges();This will also occurs when dealing with oneToMany relationship(My department is one and my employee is many).
// initialize two dbcontext
DepartAndEmp dae1 = new DepartAndEmp(); DepartAndEmp dae2 = new DepartAndEmp();
//initialize a new department Department department = new Department() { department_name = "football" };
// get a employee from dbcontext1 then the employee is traced by dbcontext1 Employee employee = dae1.employees.Find(1); // add the employee which is traced by dbcontext1 to the department department.employees.Add(employee); // use dbcontext2 to add the department which has reference to the employee which is traced by dbcontext1 dae2.Entry(department).State = System.Data.Entity.EntityState.Added; // save the department with dbcontext2 dae2.SaveChanges();You have used UnitOfWork which means all the repository in the same UnitOfWork shares the same dbcontext.
Where do you possibly use multiple dbcontext?
Do you have multiple UnitOfWork?
Please check your code which may cause the situation I listed above.
Also refer to the link which discuss the same problem.
Best regards,
Ackerly Xu
Monday, January 7, 2019 1:54 AM -
User-875744750 posted
i have used unit of work just for share one of context instance and avoid this exception.
please gave me a how to detecte that there's more than one context instance with the tools of visual studio
here's a controller action methodes code
public ActionResult _Create( int invoiceId) { Expense expense = null; Invoice dra = invoiceBll.GetById(invoiceId); ViewBag.InvoiceId = invoiceId; //rest of code return PartialView(expense); } [HttpPost] [ValidateAntiForgeryToken] public ActionResult _CreateDepense(Expense expense, int InvoiceId) { expense.Invoice= invoiceBll.GetById(invoiceId); ViewBag.InvoiceId = invoiceId; string message = ""; try { if (ModelState.IsValid) { expenseBll.AddExpense(expense, out message); TempData["AlertMessage"] = FormattedMessage.FomattedFactureErrorMessage(message); return PartialView("_ListDepensesByDRA", GetPagedListInitialzed(InvoiceId, page:1, pageSize:5)); } else { ViewBag.Message = FormattedMessage.GetFormattedMessage("insertion / modification failure", TypeMessage.Danger, true); return PartialView(expense); } } catch (BusinessLayerException ex) { ViewBag.Message = FormattedMessage.GetFormattedMessage(ex.Message, TypeMessage.Danger, true); return PartialView(expense); } catch (Exception ex) { ViewBag.Message = FormattedMessage.GetFormattedMessage("can't insert/update cette expense", TypeMessage.Danger, true); return PartialView(expense); } }
Wednesday, January 9, 2019 7:15 AM -
User-893317190 posted
Hi bensam16,
From your code, it seems you are using two bll instance?(invoiceBll and expenseBll)
Do the two bll instance share the same UnitWork?
Maybe the exception is caused by that two bll instances use different unitwork.
Please remove your try catch code and debug your code to see which line causes the problem.
Best regards,
Ackerly Xu
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Wednesday, January 9, 2019 7:39 AM -
User-875744750 posted
Do the two bll instance share the same UnitWork?i have this code
public class InvoiceBLL { UnitOfWork unitOfWork = new UnitOfWork (); // the rest of code } public class ExpenseBLL { UnitOfWork unitOfWork = new UnitOfWork (); // the rest of code }
Wednesday, January 9, 2019 10:28 AM -
User-893317190 posted
Hi bensam16,
It's a pity.They use two different UnitOfWork and so use different dbcontent.
You pass the same unitofwork to InvoiceBill and ExpenseBll.
For example:
public class ExpenseBLL { UnitOfWork unitOfWork{get;set;} // the rest of code public ExpenseBll(UnitOfWork unitofwork){ this.unitofWork = unitofwork } } public class InvoiceBLL { UnitOfWork unitOfWork{get;set;} // the rest of code public InvoiceBLL (UnitOfWork unitofwork){ this.unitofWork = unitofwork } }
And then
UnitOfWork unitofWork = new UnitOfWork(); InvoiceBll invoiceBll = new InvoiceBll(unitofWork); ExpenseBll expenseBll = new ExpenseBll(unitofWork);
Best regards,
Ackerly Xu
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Thursday, January 10, 2019 1:13 AM -
User-875744750 posted
thank you very much. it rest one question
can i implement this following code:
public abstract class BLLBase { private UniteOfWork _unitOfWork; public UniteOfWork UniteOfWork { get { return _unitOfWork ?? new UniteOfWork(); } } } public class InvoiceBLL : BLLBase { // the rest of code } public class ExpenseBLL :BLLBase { // the rest of code }
Thursday, January 10, 2019 9:13 AM -
User-893317190 posted
Hi bensam16,
Although your two bll extends the same BllBase, they still use different UnitOfWork.
Because every child class 's object will initialize their own parent class , they share different parent class's object.
You could dubug your code , set a breakpoint on return _unitOfWork ?? new UniteOfWork(); to see how many times it is called.
It will be clear whether they have the same UnitOfWork.
Best regards,
Ackerly Xu
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Thursday, January 10, 2019 9:21 AM -
User-875744750 posted
because i don't want to create a unit of work instance in controller but i want to create it in bussiness layer
is there a way to do this?
must i create unit of work for unit instance in controller?
Thursday, January 10, 2019 10:49 AM -
User753101303 posted
Hi,
An advice I saw once is to write client code first and then implement that rather than trying to design your repository/unitofwork and then having problems when you start to actually use your API.
For now the problem is that you have multiple "units" while the purpose is to have a single unit of work that manages all your repositories so that it can handle all changes across all of them.
You have multiple ways to do that (you are using dependency injection already ?). EF does that already and a simple solution could be to just have interfaces so that you can use "select" what is exposed as needed (though I'm not sure I ever saw that, it seems you are either in the "use EF raw" or "use multiple concrete repositories" camp).
Thursday, January 10, 2019 12:05 PM -
User-893317190 posted
Hi bensam16,
Maybe you could still use unitofwork in bll.
Below is my thought.
First, define bll which combine Unitofwork in bll. The Unitofwork in bll could pass Unitofwork in dal to a single bll through constructor.
public class DepartmentService { private UnitOfWork UnitOfWork { get; set; } public DepartmentService(UnitOfWork unitOfWork) { this.UnitOfWork = unitOfWork; }
// other methods using UnitOfWork } public class EmployeeService { private UnitOfWork UnitOfWork { get; set; } public EmployeeService(UnitOfWork unitOfWork) { this.UnitOfWork = unitOfWork; } }Then define unitofwork in bll.
public class UnitofWorkService { private UnitOfWork unitOfWork = new UnitOfWork(); private EmployeeService employeeService; private DepartmentService departmentService; public EmployeeService EmployeeService { get { if(this.employeeService == null) { this.employeeService = new EmployeeService(unitOfWork); } return employeeService; } } public DepartmentService DepartmentService { get { if (this.departmentService == null) { this.departmentService = new DepartmentService(unitOfWork); } return DepartmentService; } } }
Then you could use the UnitofworkService in controller instead of Unitofwork.
Best regards,
Ackerly Xu
- Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
Friday, January 11, 2019 1:38 AM