locked
An entity object cannot be referenced by multiple instances of IEntityChangeTracker with repository + unit of work RRS feed

  • 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.

    https://stackoverflow.com/questions/10191734/entity-object-cannot-be-referenced-by-multiple-instances-of-ientitychangetracker

    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