简体   繁体   中英

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

in my one application i am getting this error. Repository code


public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
    readonly IDbContext _context;
    IDbSet<TEntity> DbSet;

    public BaseRepository(IDbContext context)
    {
        _context = context;

        DbSet = context.Set<TEntity>();
    }

    #region Implementation of IRepository<TEntity>

    public TEntity FindById(object id)
    {
        return DbSet.Find(id);
    }

    public void Add(TEntity entity)
    {

        _context.Entry(entity).State=EntityState.Added;
       // DbSet.Add(entity);
    }

    public void Update(TEntity entity)
    {
        _context.Entry(entity).State = EntityState.Modified;
       // DbSet.Attach(entity);


    }

    public void Delete(TEntity entity)
    {
        if (entity == null) return;
        _context.Entry(entity).State = EntityState.Deleted;
        DbSet.Remove(entity);
    }

    public void Delete(object id)
    {
        Delete(FindById(id));
    }

    public IQueryable<TEntity> GetAll()
    {
        return DbSet;
    }



    public IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> predicate)
    {
        return DbSet.Where(predicate);
    }

    #endregion
}

UnitOfWork Code

 public class UnitOfWork : IUnitOfWork
    {
        private readonly IDbContext _context;

        private bool _disposed;
        private Hashtable _repositories;

    public UnitOfWork(IDbContext dbContext)
    {
        _context = dbContext;

    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    public void Save()
    {
        _context.SaveChanges();
    }

    public virtual void Dispose(bool disposing)
    {
        if (!_disposed)
            if (disposing)
                _context.Dispose();

        _disposed = true;
    }

    public IRepository<T> Repository<T>() where T : class
    {
        if (_repositories == null)
            _repositories = new Hashtable();

        var type = typeof(T).Name;

        if (!_repositories.ContainsKey(type))
        {
            var repositoryType = typeof(BaseRepository<>);

            var repositoryInstance =
                Activator.CreateInstance(repositoryType
                                             .MakeGenericType(typeof(T)), _context);

            _repositories.Add(type, repositoryInstance);
        }

        return (IRepository<T>)_repositories[type];
    }
}

And my Service class

 public class OrderService : IOrderService
    {
        private readonly ICoreUnitOfWork _unitOfWork;
        private readonly IRepository<Order> _repository;

        public OrderService(ICoreUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
            _repository = _unitOfWork.Repository<Order>();
            //_orderDetailsReposiotry = _unitOfWork.Repository<OrderDetails>();
        }
        #region Implementation of IOrderService

        public void PlaceNewOrder(Order order)
        {
            if (order.Detailses.Count == 0) throw new ArgumentException("Sorry! your order doesn't contains any item");
            _repository.Add(order);

            _unitOfWork.Save();
            _unitOfWork.Dispose(true);
        }

        #endregion
    }

Controller Code

[HttpPost]
        public ViewResult OrderSummury(Cart cart, ShippingAddress address)
        {
            if (cart.Lines.Count == 0)
            {
                TempData["result"] = string.Format(" Sorry! your cart is empty");
                return View(new ShippingAddress());
            }
            try
            {
                _distributer = _distributerService.GetDistributerbyEmail(User.Identity.Name);
                var order = new Order();
                order.OrderCreated = DateTime.Now.Date;
                var refrenceArray = Guid.NewGuid().ToString().Split('-');
                order.ReferenceCode += string.Format("{0}{1}", refrenceArray[1], refrenceArray[2]);
                order.Status = 0;
                order.ShippingAddress = address.AddressLine;
                order.ShippingContactPerson = address.ContactPerson;
                order.ShippingMobile = address.ShippingMobile;

                order.Distributer = _distributer;

                foreach (var line in cart.Lines)
                {
                    order.AddOrderItem(new OrderDetails {Product  =line.Product,  Quantity = line.Quantity, UnitPrice = line.Product.Price * line.Quantity });
                }
                _orderService.PlaceNewOrder(order);
                cart.Clear();

                TempData["result"] = string.Format(" Order place with  code {0}  successfully", order.ReferenceCode);
            }
            catch (ArgumentException exception)
            {
                TempData["result"] = exception.Message;
            }
            catch (Exception e)
            {
                TempData["result"] = "An unknown error occurred";

                LoggingFactory.GetLogger().LogError("Order registration", e);

            }



            return View(address);
        }

My Problem

When i execute the above codes i am getting an error with message An entity object cannot be referenced by multiple instances of IEntityChangeTracker. I checked everything and i found the following findings

1.) The error caused when i am referencing product entity order.AddOrderItem(new OrderDetails {Product =line.Product

2.)In my controller Product entities are loading from session ( I am using my own custommodel binder for reading values from session and it is working well ).

Why i am getting this error ? Any suggession

On top of the generic repository, you need to use a unit of work in order to avoid the multiple context references on the entities. Basically, the unit of work should allow many repositories to work together under the same context. I do it something like this:

public class UnitOfWork : IUnitOfWork
{
    private readonly MyContext _context;

    public UnitOfWork()
    {
        _context = new MyContext();
    }

    public void Save()
    {
        _context.SaveChanges();
    }

    private bool disposed = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                _context.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    public MyContext Context
    {
        get { return _context; }
    }
}

Then the generic repo:

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
    private readonly UnitOfWork _unitOfWork;

    public Repository() : this(new UnitOfWork())
    {
    }

    public Repository(UnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork ?? new UnitOfWork();
    }

    public IList<TEntity> GetAll()
    {
        try
        {
            return _unitOfWork.context.Set<TEntity>().ToList();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
            return Enumerable.Empty;
        }
    }

    public TEntity GetById(int id)
    {
        try
        {
            return _unitOfWork.context.Set<TEntity>().Find(id);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
            return null;
        }
    }

    public TEntity Get(TEntity entity)
    {
        try
        {
            return _unitOfWork.context.Set<TEntity>().Find(entity);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
            return null;
        }
    }

    public bool Add(TEntity entity)
    {
        try
        {
            _unitOfWork.context.Set<TEntity>().Add(entity);
            _unitOfWork.Save();
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
            return false;
        }
    }

    public bool Update(TEntity entity)
    {
        try
        {
            _unitOfWork.context.Set<TEntity>().Attach(entity);
            _unitOfWork.context.Entry(entity).State = EntityState.Modified;
            _unitOfWork.Save();
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
            return false;
        }
    }

    public bool Delete(TEntity entity)
    {
        try
        {
            var entityToRemove = Get(entity);
            _unitOfWork.context.Set<TEntity>().Remove(entityToRemove);
            _unitOfWork.Save();
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
            return false;
        }
    }

    public bool DeleteById(int id)
    {
        try
        {
            var entityToRemove = GetById(id);
            _unitOfWork.context.Set<TEntity>().Remove(entityToRemove);
            _unitOfWork.Save();
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.StackTrace);
            return false;
        }
    }
   }

When you want to use multiple repositories within the same context, you just instantiate the respective repositories with the same unit of work:

UnitOfWork uo = new UnitOfWork();

FooRepository example1 = new FooRepository(uo);

BarRepository example2 = new BarRepository(uo);

// do operations...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM