2

I use EF 4.1 with Repository and DbContext.. POCO with T4 template. For every Repository I use a separate DbContext.

I need to update an object with has a related property, at the moment I receive this error

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

I suppose my problem is beacuse eventObj and candidate are created from different Repositories.

So i'm trying to solve the problem with this code, with no success.

My question?

  • How do I get rid of this error?
  • Is is possible remove the candidate from its context?

    public void UpdateAddingCandidate(Event eventObj, Candidate candidate){
        Event updatedEvent = new Event();
        Candidate updatedCandidate = new Candidate();
        updatedEvent = eventObj;
        updatedCandidate = candidate;
        updatedEvent.Candidate = updatedCandidate;
        db.Entry(updatedEvent).State = EntityState.Modified;     
    }
    

EDIT

    public void UpdateAddingCandidate(Event eventObj, Candidate candidate)
    {
        /*
        db.Events.AsNoTracking();
        db.Candidates.AsNoTracking();
        */
        db.Entry(eventObj).State = EntityState.Detached;
        db.Entry(candidate).State = EntityState.Detached;

        Event updatedEvent = new Event();
        Candidate updatedCandidate = new Candidate();
        updatedEvent = eventObj;
        updatedCandidate = candidate;
        updatedEvent.Candidate = updatedCandidate;
        db.Entry(updatedEvent).State = EntityState.Detached;
        db.Entry(updatedEvent).State = EntityState.Modified;

    }
GibboK
  • 71,848
  • 143
  • 435
  • 658

3 Answers3

15

Instead of only using the repository pattern also use the Unit of Work pattern. That way you have 1 point for every entity you use.

Data/Contracts/IRepository.cs

namespace Data.Contracts
{
    public interface IRepository<T> where T : class
    {
        IQueryable<T> GetAll();
        T GetById(int id);
        void Add(T entity);
        void Update(T entity);
        void Delete(T entity);
        void Delete(int id);
}

Data/Contracts/IUnitOfWork.cs

namespace Data.Contracts
{
    /// <summary>
    /// Interface for the "Unit of Work"
    /// </summary>
    public interface IUnitOfWork
    {
        // Save pending changes to the data store.
        void Commit();

        // Repositories
        IRepository<Event> Events { get; }
        IRepository<Candidate> Candidates { get; }
    }
}

Data/EFRepository.cs

using System;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using Data.Contracts;

namespace Data
{
    /// <summary>
    /// The EF-dependent, generic repository for data access
    /// </summary>
    /// <typeparam name="T">Type of entity for this Repository.</typeparam>
    public class EFRepository<T> : IRepository<T> where T : class
    {
        public EFRepository(DbContext dbContext)
        {
            if (dbContext == null)
                throw new ArgumentNullException("dbContext");
            DbContext = dbContext;
            DbSet = DbContext.Set<T>();
        }

        protected DbContext DbContext { get; set; }

        protected DbSet<T> DbSet { get; set; }

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

        public virtual T GetById(int id)
        {
            //return DbSet.FirstOrDefault(PredicateBuilder.GetByIdPredicate<T>(id));
            return DbSet.Find(id);
        }

        public virtual void Add(T entity)
        {
            DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
            if (dbEntityEntry.State != EntityState.Detached)
            {
                dbEntityEntry.State = EntityState.Added;
            }
            else
            {
                DbSet.Add(entity);
            }
        }

        public virtual void Update(T entity)
        {
            DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
            if (dbEntityEntry.State == EntityState.Detached)
            {
                DbSet.Attach(entity);
            }
            dbEntityEntry.State = EntityState.Modified;
        }

        public virtual void Delete(T entity)
        {
            DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
            if (dbEntityEntry.State != EntityState.Deleted)
            {
                dbEntityEntry.State = EntityState.Deleted;
            }
            else
            {
                DbSet.Attach(entity);
                DbSet.Remove(entity);
            }
        }

        public virtual void Delete(int id)
        {
            var entity = GetById(id);
            if (entity == null) return; // not found; assume already deleted.
            Delete(entity);
        }
    }
}

Data/UnitOfWork.cs

using System;
using Data.Contracts;
using Data.Helpers;
using Models;

namespace Data
{
    /// <summary>
    /// The "Unit of Work"
    ///     1) decouples the repos from the controllers
    ///     2) decouples the DbContext and EF from the controllers
    ///     3) manages the UoW
    /// </summary>
    /// <remarks>
    /// This class implements the "Unit of Work" pattern in which
    /// the "UoW" serves as a facade for querying and saving to the database.
    /// Querying is delegated to "repositories".
    /// Each repository serves as a container dedicated to a particular
    /// root entity type such as a <see cref="Url"/>.
    /// A repository typically exposes "Get" methods for querying and
    /// will offer add, update, and delete methods if those features are supported.
    /// The repositories rely on their parent UoW to provide the interface to the
    /// data layer (which is the EF DbContext in this example).
    /// </remarks>
    public class UnitOfWork : IUnitOfWork, IDisposable
    {
        public UnitOfWork(IRepositoryProvider repositoryProvider)
        {
            CreateDbContext();

            repositoryProvider.DbContext = DbContext;
            RepositoryProvider = repositoryProvider;       
        }

        // Repositories
        public IRepository<Event> Events { get { return GetStandardRepo<Event>(); } }
        public IRepository<Candidate> Candidates { get { return GetStandardRepo<Candidate>(); } }

        /// <summary>
        /// Save pending changes to the database
        /// </summary>
        public void Commit()
        {
            //System.Diagnostics.Debug.WriteLine("Committed");
            DbContext.SaveChanges();
        }

        protected void CreateDbContext()
        {
            DbContext = new UnicornsContext();
        }

        protected IRepositoryProvider RepositoryProvider { get; set; }

        private IRepository<T> GetStandardRepo<T>() where T : class
        {
            return RepositoryProvider.GetRepositoryForEntityType<T>();
        }
        private T GetRepo<T>() where T : class
        {
            return RepositoryProvider.GetRepository<T>();
        }

        private UnicornsContext DbContext { get; set; }

        #region IDisposable

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

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (DbContext != null)
                {
                    DbContext.Dispose();
                }
            }
        }

        #endregion
    }
}

Data/Helpers/IRepositoryProvider.cs

using System;
using System.Data.Entity;
using Data.Contracts;

namespace Data.Helpers
{
    /// <summary>
    /// Interface for a class that can provide repositories by type.
    /// The class may create the repositories dynamically if it is unable
    /// to find one in its cache of repositories.
    /// </summary>
    /// <remarks>
    /// Repositories created by this provider tend to require a <see cref="DbContext"/>
    /// to retrieve data.
    /// </remarks>
    public interface IRepositoryProvider
    {
        /// <summary>
        /// Get and set the <see cref="DbContext"/> with which to initialize a repository
        /// if one must be created.
        /// </summary>
        DbContext DbContext { get; set; }

        /// <summary>
        /// Get an <see cref="IRepository{T}"/> for entity type, T.
        /// </summary>
        /// <typeparam name="T">
        /// Root entity type of the <see cref="IRepository{T}"/>.
        /// </typeparam>
        IRepository<T> GetRepositoryForEntityType<T>() where T : class;

        /// <summary>
        /// Get a repository of type T.
        /// </summary>
        /// <typeparam name="T">
        /// Type of the repository, typically a custom repository interface.
        /// </typeparam>
        /// <param name="factory">
        /// An optional repository creation function that takes a <see cref="DbContext"/>
        /// and returns a repository of T. Used if the repository must be created.
        /// </param>
        /// <remarks>
        /// Looks for the requested repository in its cache, returning if found.
        /// If not found, tries to make one with the factory, fallingback to 
        /// a default factory if the factory parameter is null.
        /// </remarks>
        T GetRepository<T>(Func<DbContext, object> factory = null) where T : class;


        /// <summary>
        /// Set the repository to return from this provider.
        /// </summary>
        /// <remarks>
        /// Set a repository if you don't want this provider to create one.
        /// Useful in testing and when developing without a backend
        /// implementation of the object returned by a repository of type T.
        /// </remarks>
        void SetRepository<T>(T repository);
    }
}

Data/Helpers/RepositoryProvider.cs

using System;
using System.Collections.Generic;
using System.Data.Entity;
using Data.Contracts;

namespace Data.Helpers
{
    /// <summary>
    /// Provides an <see cref="IRepository{T}"/> for a client request.
    /// </summary>
    /// <remarks>
    /// Caches repositories of a given type so that repositories are only created once per provider.
    /// There should be a a new provider per client request.
    /// </remarks>
    public class RepositoryProvider : IRepositoryProvider
    {
        public RepositoryProvider(RepositoryFactories repositoryFactories)
        {
            _repositoryFactories = repositoryFactories;
            Repositories = new Dictionary<Type, object>();
        }

        /// <summary>
        /// Get and set the <see cref="DbContext"/> with which to initialize a repository
        /// if one must be created.
        /// </summary>
        public DbContext DbContext { get; set; }

        /// <summary>
        /// Get or create-and-cache the default <see cref="IRepository{T}"/> for an entity of type T.
        /// </summary>
        /// <typeparam name="T">
        /// Root entity type of the <see cref="IRepository{T}"/>.
        /// </typeparam>
        /// <remarks>
        /// If can't find repository in cache, use a factory to create one.
        /// </remarks>
        public IRepository<T> GetRepositoryForEntityType<T>() where T : class
        {
            return GetRepository<IRepository<T>>(
                _repositoryFactories.GetRepositoryFactoryForEntityType<T>());
        }

        /// <summary>
        /// Get or create-and-cache a repository of type T.
        /// </summary>
        /// <typeparam name="T">
        /// Type of the repository, typically a custom repository interface.
        /// </typeparam>
        /// <param name="factory">
        /// An optional repository creation function that takes a DbContext argument
        /// and returns a repository of T. Used if the repository must be created and
        /// caller wants to specify the specific factory to use rather than one
        /// of the injected <see cref="RepositoryFactories"/>.
        /// </param>
        /// <remarks>
        /// Looks for the requested repository in its cache, returning if found.
        /// If not found, tries to make one using <see cref="MakeRepository{T}"/>.
        /// </remarks>
        public virtual T GetRepository<T>(Func<DbContext, object> factory = null) where T : class
        {
            // Look for T dictionary cache under typeof(T).
            object repoObj;
            Repositories.TryGetValue(typeof(T), out repoObj);
            if (repoObj != null)
            {
                return (T)repoObj;
            }

            // Not found or null; make one, add to dictionary cache, and return it.
            return MakeRepository<T>(factory, DbContext);
        }

        /// <summary>
        /// Get the dictionary of repository objects, keyed by repository type.
        /// </summary>
        /// <remarks>
        /// Caller must know how to cast the repository object to a useful type.
        /// <p>This is an extension point. You can register fully made repositories here
        /// and they will be used instead of the ones this provider would otherwise create.</p>
        /// </remarks>
        protected Dictionary<Type, object> Repositories { get; private set; }

        /// <summary>Make a repository of type T.</summary>
        /// <typeparam name="T">Type of repository to make.</typeparam>
        /// <param name="dbContext">
        /// The <see cref="DbContext"/> with which to initialize the repository.
        /// </param>        
        /// <param name="factory">
        /// Factory with <see cref="DbContext"/> argument. Used to make the repository.
        /// If null, gets factory from <see cref="_repositoryFactories"/>.
        /// </param>
        /// <returns></returns>
        protected virtual T MakeRepository<T>(Func<DbContext, object> factory, DbContext dbContext)
        {
            var f = factory ?? _repositoryFactories.GetRepositoryFactory<T>();
            if (f == null)
            {
                throw new NotImplementedException("No factory for repository type, " + typeof(T).FullName);
            }
            var repo = (T)f(dbContext);
            Repositories[typeof(T)] = repo;
            return repo;
        }

        /// <summary>
        /// Set the repository for type T that this provider should return.
        /// </summary>
        /// <remarks>
        /// Plug in a custom repository if you don't want this provider to create one.
        /// Useful in testing and when developing without a backend
        /// implementation of the object returned by a repository of type T.
        /// </remarks>
        public void SetRepository<T>(T repository)
        {
            Repositories[typeof(T)] = repository;
        }

        /// <summary>
        /// The <see cref="RepositoryFactories"/> with which to create a new repository.
        /// </summary>
        /// <remarks>
        /// Should be initialized by constructor injection
        /// </remarks>
        private RepositoryFactories _repositoryFactories;
    }
}

Data/Helpers/RepositoryFactories.cs

using System;
using System.Collections.Generic;
using System.Data.Entity;
using Data.Contracts;

namespace Data.Helpers
{
    /// <summary>
    /// A maker of Repositories.
    /// </summary>
    /// <remarks>
    /// An instance of this class contains repository factory functions for different types.
    /// Each factory function takes an EF <see cref="DbContext"/> and returns
    /// a repository bound to that DbContext.
    /// <para>
    /// Designed to be a "Singleton", configured at web application start with
    /// all of the factory functions needed to create any type of repository.
    /// Should be thread-safe to use because it is configured at app start,
    /// before any request for a factory, and should be immutable thereafter.
    /// </para>
    /// </remarks>
    public class RepositoryFactories
    {
        /// <summary>
        /// Return the runtime repository factory functions,
        /// each one is a factory for a repository of a particular type.
        /// </summary>
        /// <remarks>
        /// MODIFY THIS METHOD TO ADD CUSTOM FACTORY FUNCTIONS
        /// </remarks>
        private IDictionary<Type, Func<DbContext, object>> GetFactories()
        {
            return new Dictionary<Type, Func<DbContext, object>>
                {
                   //If you have an custom implementation of an IRepository<T>
                   //{typeof(IArticleRepository), dbContext => new ArticleRepository(dbContext)}
                };
        }

        /// <summary>
        /// Constructor that initializes with runtime repository factories
        /// </summary>
        public RepositoryFactories()
        {
            _repositoryFactories = GetFactories();
        }

        /// <summary>
        /// Constructor that initializes with an arbitrary collection of factories
        /// </summary>
        /// <param name="factories">
        /// The repository factory functions for this instance. 
        /// </param>
        /// <remarks>
        /// This ctor is primarily useful for testing this class
        /// </remarks>
        public RepositoryFactories(IDictionary<Type, Func<DbContext, object>> factories)
        {
            _repositoryFactories = factories;
        }

        /// <summary>
        /// Get the repository factory function for the type.
        /// </summary>
        /// <typeparam name="T">Type serving as the repository factory lookup key.</typeparam>
        /// <returns>The repository function if found, else null.</returns>
        /// <remarks>
        /// The type parameter, T, is typically the repository type 
        /// but could be any type (e.g., an entity type)
        /// </remarks>
        public Func<DbContext, object> GetRepositoryFactory<T>()
        {

            Func<DbContext, object> factory;
            _repositoryFactories.TryGetValue(typeof(T), out factory);
            return factory;
        }

        /// <summary>
        /// Get the factory for <see cref="IRepository{T}"/> where T is an entity type.
        /// </summary>
        /// <typeparam name="T">The root type of the repository, typically an entity type.</typeparam>
        /// <returns>
        /// A factory that creates the <see cref="IRepository{T}"/>, given an EF <see cref="DbContext"/>.
        /// </returns>
        /// <remarks>
        /// Looks first for a custom factory in <see cref="_repositoryFactories"/>.
        /// If not, falls back to the <see cref="DefaultEntityRepositoryFactory{T}"/>.
        /// You can substitute an alternative factory for the default one by adding
        /// a repository factory for type "T" to <see cref="_repositoryFactories"/>.
        /// </remarks>
        public Func<DbContext, object> GetRepositoryFactoryForEntityType<T>() where T : class
        {
            return GetRepositoryFactory<T>() ?? DefaultEntityRepositoryFactory<T>();
        }

        /// <summary>
        /// Default factory for a <see cref="IRepository{T}"/> where T is an entity.
        /// </summary>
        /// <typeparam name="T">Type of the repository's root entity</typeparam>
        protected virtual Func<DbContext, object> DefaultEntityRepositoryFactory<T>() where T : class
        {
            return dbContext => new EFRepository<T>(dbContext);
        }

        /// <summary>
        /// Get the dictionary of repository factory functions.
        /// </summary>
        /// <remarks>
        /// A dictionary key is a System.Type, typically a repository type.
        /// A value is a repository factory function
        /// that takes a <see cref="DbContext"/> argument and returns
        /// a repository object. Caller must know how to cast it.
        /// </remarks>
        private readonly IDictionary<Type, Func<DbContext, object>> _repositoryFactories;
    }
}

Now if you have this to use your UnitOfWork you can't simply call it because of all the dependencies, if you have used an IoC container before you know that you can simply resolve those dependencies. In my case I used Ninject so the code to set it up would be:

var kernel = new StandardKernel(); // Ninject IoC

kernel.Bind<RepositoryFactories>().To<RepositoryFactories>().InSingletonScope();
kernel.Bind<IRepositoryProvider>().To<RepositoryProvider>();
kernel.Bind<IUnitOfWork>().To<UnitOfWork>();

Then for your pages you can add a base page class:

Web.BasePage.cs

namespace Web
{
    public abstract class BasePage : System.Web.UI.Page
    {
        // NOT NECESSARY TO DISPOSE THE UOW IN OUR CONTROLLERS
        // Recall that we let IoC inject the Uow into our controllers
        // We can depend upon on IoC to dispose the UoW for us
        protected IUnitOfWork Uow { get; set; }
    }
}

Or in the case of MVC you can add a base controller:

Web/Controllers/BaseController.cs

using System.Web.Mvc;

namespace Site.Controllers
{
    public abstract class BaseController : Controller
    {
        // NOT NECESSARY TO DISPOSE THE UOW IN OUR CONTROLLERS
        // Recall that we let IoC inject the Uow into our controllers
        // We can depend upon on IoC to dispose the UoW for us
        protected IUnitOfWork Uow { get; set; }
    }
}

Then in your code when you want to access you entities it is very easy. You can simply call the UnitOfWork which is available in every page and access the different repositories in it. Because they all share the same DbContext your problem ceases to exist.


I have made a very simple example of the Unit of Work pattern with support of Ninject for the Inversion of Control.

In my example there is a ASP.NET Webforms AND and ASP.NET MVC4 example

The example is a single page site and every time you load the site it will add an Event to the example database and and Category to the Database, both with random names. (I needed something for the DB so I just made 2 simple pocos). After that the page will display everything in in both lists. Just look into the Mvc4/Controllers/HomeController.cs or WebForms/Default.aspx.cs.

Example on skydrive

SynerCoder
  • 12,493
  • 4
  • 47
  • 78
  • What is `EFRepository`? Can you post a gist of full working code? – Mrchief Oct 11 '12 at 08:46
  • @Mrchief true true, I forgot the EFRepository, it is an implementation of IRepository, ill add in a sec – SynerCoder Oct 11 '12 at 08:48
  • @Mrchief would you like a webforms or a mvc example? – SynerCoder Oct 11 '12 at 08:49
  • @Mrchief added single solution with both examples – SynerCoder Oct 11 '12 at 14:09
  • I thought your code wouldn't compile properly so I started playing with it. To my surprise, not only it does, it works like a charm! I plugged in my EFRepository thinking that's what you had meant anyway and I see that your code isn't too different either. I was going to create a gist until I saw your update which is much better! +1 for recommending the right pattern and having code that is neatly explained as well! – Mrchief Oct 11 '12 at 18:18
  • I stumbled upon this thread looking for a different problem - Ninject + Web Api + Filter Injection = Rocket Science! But I liked your answer! – Mrchief Oct 12 '12 at 01:01
  • One question though - why do you turn off lazy loading? That could kill the performance especially if you have lots of child records and you don't use them on every call. – Mrchief Oct 12 '12 at 01:29
  • @Mrchief I derived this example from a webapi project I am working on. In webapi for the serialization lazy loading can become a pain in the ass, but for normal use I agree with you. – SynerCoder Oct 12 '12 at 08:32
  • That's interesting coz I use Web Api with JSON.Net handling all serialization for me and I haven't had any issues. – Mrchief Oct 12 '12 at 14:16
  • @Mrchief It isn't always an issue, but if you want to pass for example an user, which has a relation to an address it might not show up since there is no explicit call to the address, but if you disable lazy loading you are forced to tell EF to also load the address property of the user. But as always, it depends on your situation – SynerCoder Oct 12 '12 at 14:29
  • You can always load them explicitly for situations like that. For most part, ideally, you do not want to load the children. I would agree that it depends on your situation, but then, if you need to load children every time, that would be a smell to me. But then again, your UnitOfWork fails to load children for even with that disabled. – Mrchief Oct 12 '12 at 15:18
  • How do you mean? If you enable lazy loading, it will still works like a charm, the DbContext DbSets are just mapped to the different repos, if you ask for address from user the user will use the DbContext, the same DbContext as the AddressRepo would use. Therefor no failing – SynerCoder Oct 12 '12 at 15:29
  • Well I cannot get to the child objects either way. I guess I must have screwed up somewhere else. – Mrchief Oct 12 '12 at 17:48
  • Great implementation! But what if you need to inject some transient or scoped services into EFRepository? If I change Dictionary to "Func" would that be resolved correctly? I don't realy get it how dependencies resolves in that particular case from singletone. – Ice2burn May 25 '21 at 13:54
1

This error is indeed thrown when the entity you're trying to update is attached to a different objectcontext than the context you're using to update it. Only one objectcontext can track an entity

You can detach objects from it's context by calling:

where context is the context the entity currently is attached to.

using (var context = new UnicornsContext())
{
    var unicorn = new Unicorn { Name = "Franky", PrincessId = 1};
    context.Entry(unicorn).State = EntityState.Detached;
    context.SaveChanges();
}

http://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx

And in your implementation:

public void UpdateAddingCandidate(Event eventObj, Candidate candidate)
{
    db.Entry(candidate).State = EntityState.Detached;
    db.SaveChanges();

    eventObj.Candidate = candidate;
    db.Entry(eventObj).State = EntityState.Modified;
    db.SaveChanges();
}
middelpat
  • 2,555
  • 1
  • 20
  • 29
0

Why do you create new instance of Event and Candidate? You could just change the state of these you already have them. If you share the same instance of ObjectContext accross your repositories, you can try to resolve these entities from context with ObjectStateManager.GetObjectStateEntry(string key) ObjectStateManager.

Eduard
  • 3,176
  • 3
  • 21
  • 31