Как отключить сущность из контекста в Entity Framework?
Я использую EF 4.1 с репозиторием и DbContext .. POCO с шаблоном T4. Для каждого репозитория я использую отдельный DbContext.
Мне нужно обновить объект с соответствующим свойством, на данный момент я получаю эту ошибку
An entity object cannot be referenced by multiple instances of IEntityChangeTracker.
Я полагаю, что моя проблема – eventObj
а candidate
создается из разных Хранилищ.
- Выполнение case-statement в структуре агрегации mongodb
- Итерация над структурой в C ++
- Как фильтровать массив в подзадаче с помощью MongoDB
- Как выглядит заголовок zlib?
- Реализация комментариев и комментариев в базе данных
Поэтому я пытаюсь решить проблему с этим кодом, без успеха.
Мой вопрос?
- Как я могу избавиться от этой ошибки?
-
Возможно ли удалить кандидата из его контекста?
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; }
РЕДАКТИРОВАТЬ
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; }
- Несколько условий соединения с помощью оператора $ lookup
- Наследование структуры в C
- Включить все существующие поля и добавить новые поля в документ
- Структура агрегации Mongodb: используется ли индекс использования группы?
- Solr документы с дочерними элементами?
- Возвращать только согласованные элементы субдокумента внутри вложенного массива
- Получить n-й элемент массива в MongoDB
- Может ли встроенный метод struct знать знания родителя / ребенка?
Эта ошибка действительно возникает, когда объект, который вы пытаетесь обновить, привязан к другому объекту contexttext, чем контекст, который вы используете для его обновления. Только один объектконтекст может отслеживать объект
Вы можете отделить объекты от контекста, вызвав:
где контекст – это контекст, к которому привязана сущность.
using (var context = new UnicornsContext()) { var unicorn = new Unicorn { Name = "Franky", PrincessId = 1}; context.Entry(unicorn).State = EntityState.Detached; context.SaveChanges(); }
И в вашей реализации:
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(); }
Вместо использования шаблона репозитория также используйте шаблон «Единица работы». Таким образом, у вас есть 1 балл для каждого объекта, который вы используете.
Данные / Контракты / IRepository.cs
namespace Data.Contracts { public interface IRepository where T : class { IQueryable GetAll(); T GetById(int id); void Add(T entity); void Update(T entity); void Delete(T entity); void Delete(int id); }
Данные / Контракты / IUnitOfWork.cs
namespace Data.Contracts { /// /// Interface for the "Unit of Work" /// public interface IUnitOfWork { // Save pending changes to the data store. void Commit(); // Repositories IRepository Events { get; } IRepository Candidates { get; } } }
Данные / EFRepository.cs
using System; using System.Data; using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Linq; using Data.Contracts; namespace Data { /// /// The EF-dependent, generic repository for data access /// /// Type of entity for this Repository. public class EFRepository : IRepository where T : class { public EFRepository(DbContext dbContext) { if (dbContext == null) throw new ArgumentNullException("dbContext"); DbContext = dbContext; DbSet = DbContext.Set (); } protected DbContext DbContext { get; set; } protected DbSet DbSet { get; set; } public virtual IQueryable GetAll() { return DbSet; } public virtual T GetById(int id) { //return DbSet.FirstOrDefault(PredicateBuilder.GetByIdPredicate (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); } } }
Данные / UnitOfWork.cs
using System; using Data.Contracts; using Data.Helpers; using Models; namespace Data { /// /// The "Unit of Work" /// 1) decouples the repos from the controllers /// 2) decouples the DbContext and EF from the controllers /// 3) manages the UoW /// /// /// 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 . /// 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). /// public class UnitOfWork : IUnitOfWork, IDisposable { public UnitOfWork(IRepositoryProvider repositoryProvider) { CreateDbContext(); repositoryProvider.DbContext = DbContext; RepositoryProvider = repositoryProvider; } // Repositories public IRepository Events { get { return GetStandardRepo (); } } public IRepository Candidates { get { return GetStandardRepo (); } } /// /// Save pending changes to the database /// public void Commit() { //System.Diagnostics.Debug.WriteLine("Committed"); DbContext.SaveChanges(); } protected void CreateDbContext() { DbContext = new UnicornsContext(); } protected IRepositoryProvider RepositoryProvider { get; set; } private IRepository GetStandardRepo () where T : class { return RepositoryProvider.GetRepositoryForEntityType (); } private T GetRepo () where T : class { return RepositoryProvider.GetRepository (); } 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 } }
Данные / Помощники / IRepositoryProvider.cs
using System; using System.Data.Entity; using Data.Contracts; namespace Data.Helpers { /// /// 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. /// /// /// Repositories created by this provider tend to require a /// to retrieve data. /// public interface IRepositoryProvider { /// /// Get and set the with which to initialize a repository /// if one must be created. /// DbContext DbContext { get; set; } /// /// Get an for entity type, T. /// /// /// Root entity type of the . /// IRepository GetRepositoryForEntityType () where T : class; /// /// Get a repository of type T. /// /// /// Type of the repository, typically a custom repository interface. /// /// /// An optional repository creation function that takes a /// and returns a repository of T. Used if the repository must be created. /// /// /// 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. /// T GetRepository (Func factory = null) where T : class; /// /// Set the repository to return from this provider. /// /// /// 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. /// void SetRepository (T repository); } }
Данные / Помощники / RepositoryProvider.cs
using System; using System.Collections.Generic; using System.Data.Entity; using Data.Contracts; namespace Data.Helpers { /// /// Provides an for a client request. /// /// /// Caches repositories of a given type so that repositories are only created once per provider. /// There should be aa new provider per client request. /// public class RepositoryProvider : IRepositoryProvider { public RepositoryProvider(RepositoryFactories repositoryFactories) { _repositoryFactories = repositoryFactories; Repositories = new Dictionary(); } /// /// Get and set the with which to initialize a repository /// if one must be created. /// public DbContext DbContext { get; set; } /// /// Get or create-and-cache the default for an entity of type T. /// /// /// Root entity type of the . /// /// /// If can't find repository in cache, use a factory to create one. /// public IRepository GetRepositoryForEntityType () where T : class { return GetRepository>( _repositoryFactories.GetRepositoryFactoryForEntityType ()); } /// /// Get or create-and-cache a repository of type T. /// /// /// Type of the repository, typically a custom repository interface. /// /// /// 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 . /// /// /// Looks for the requested repository in its cache, returning if found. /// If not found, tries to make one using . /// public virtual T GetRepository (Func 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 (factory, DbContext); } /// /// Get the dictionary of repository objects, keyed by repository type. /// /// /// Caller must know how to cast the repository object to a useful type. /// 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.
/// protected Dictionary Repositories { get; private set; } /// Make a repository of type T. /// Type of repository to make. /// /// The with which to initialize the repository. /// /// /// Factory with argument. Used to make the repository. /// If null, gets factory from . /// /// protected virtual T MakeRepository (Func factory, DbContext dbContext) { var f = factory ?? _repositoryFactories.GetRepositoryFactory (); 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; } /// /// Set the repository for type T that this provider should return. /// /// /// 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. /// public void SetRepository (T repository) { Repositories[typeof(T)] = repository; } /// /// The with which to create a new repository. /// /// /// Should be initialized by constructor injection /// private RepositoryFactories _repositoryFactories; } }
Данные / Помощники / RepositoryFactories.cs
using System; using System.Collections.Generic; using System.Data.Entity; using Data.Contracts; namespace Data.Helpers { /// /// A maker of Repositories. /// /// /// An instance of this class contains repository factory functions for different types. /// Each factory function takes an EF and returns /// a repository bound to that DbContext. /// /// 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. /// /// public class RepositoryFactories { /// /// Return the runtime repository factory functions, /// each one is a factory for a repository of a particular type. /// /// /// MODIFY THIS METHOD TO ADD CUSTOM FACTORY FUNCTIONS /// private IDictionary> GetFactories() { return new Dictionary> { //If you have an custom implementation of an IRepository //{typeof(IArticleRepository), dbContext => new ArticleRepository(dbContext)} }; } /// /// Constructor that initializes with runtime repository factories /// public RepositoryFactories() { _repositoryFactories = GetFactories(); } /// /// Constructor that initializes with an arbitrary collection of factories /// /// /// The repository factory functions for this instance. /// /// /// This ctor is primarily useful for testing this class /// public RepositoryFactories(IDictionary> factories) { _repositoryFactories = factories; } /// /// Get the repository factory function for the type. /// /// Type serving as the repository factory lookup key. /// The repository function if found, else null. /// /// The type parameter, T, is typically the repository type /// but could be any type (eg, an entity type) /// public Func GetRepositoryFactory () { Func factory; _repositoryFactories.TryGetValue(typeof(T), out factory); return factory; } /// /// Get the factory for where T is an entity type. /// /// The root type of the repository, typically an entity type. /// /// A factory that creates the , given an EF . /// /// /// Looks first for a custom factory in . /// If not, falls back to the . /// You can substitute an alternative factory for the default one by adding /// a repository factory for type "T" to . /// public Func GetRepositoryFactoryForEntityType () where T : class { return GetRepositoryFactory () ?? DefaultEntityRepositoryFactory (); } /// /// Default factory for a where T is an entity. /// /// Type of the repository's root entity protected virtual Func DefaultEntityRepositoryFactory () where T : class { return dbContext => new EFRepository (dbContext); } /// /// Get the dictionary of repository factory functions. /// /// /// A dictionary key is a System.Type, typically a repository type. /// A value is a repository factory function /// that takes a argument and returns /// a repository object. Caller must know how to cast it. /// private readonly IDictionary> _repositoryFactories; } }
Теперь, если у вас есть это для использования UnitOfWork, вы не можете просто вызвать его из-за всех зависимостей, если вы использовали контейнер IoC, прежде чем знаете, что можете просто разрешить эти зависимости. В моем случае я использовал Ninject, поэтому код для его настройки:
var kernel = new StandardKernel(); // Ninject IoC kernel.Bind().To ().InSingletonScope(); kernel.Bind().To(); kernel.Bind().To();
Затем для ваших страниц вы можете добавить 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; } } }
Или в случае MVC вы можете добавить базовый controller:
Web / Контроллеры / 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; } } }
Тогда в вашем коде, когда вы хотите получить доступ к своим сущностям, это очень просто. Вы можете просто вызвать UnitOfWork, который доступен на каждой странице и получить доступ к различным хранилищам в нем. Поскольку все они имеют один и тот же DbContext, ваша проблема перестает существовать.
Я сделал очень простой пример шаблона Unit of Work с поддержкой Ninject for Inversion of Control.
В моем примере есть пример ASP.NET Webforms AND и ASP.NET MVC4
Пример – это сайт с одной страницей, и каждый раз, когда вы загружаете сайт, он добавляет событие в базу данных примера и категорию в базу данных, как со случайными именами. (Мне нужно что-то для БД, поэтому я просто сделал 2 простых pocos). После этого на странице будет отображаться все в обоих списках. Просто загляните в Mvc4/Controllers/HomeController.cs
или WebForms/Default.aspx.cs
.
Пример по skydrive
Почему вы создаете новый экземпляр Event
и Candidate
? Вы можете просто изменить состояние, которое у вас уже есть. Если вы используете один и тот же экземпляр объекта ObjectContext
своих репозиториев, вы можете попытаться разрешить эти объекты из контекста с помощью ObjectStateManager.GetObjectStateEntry(string key)
ObjectStateManager .