Entity Framework Code-First: «ObjectStateManager не может отслеживать несколько объектов с одним и тем же ключом». - PullRequest
5 голосов
/ 08 марта 2012

Я столкнулся с проблемой с кодом Entity Framework сначала в MVC3.Я бью это исключение:

Объект с таким же ключом уже существует в ObjectStateManager.ObjectStateManager не может отслеживать несколько объектов с одним и тем же ключом.

Это многократно решается в SO, но у меня возникают проблемы с использованием любого из предложенных решений в моей ситуации.

Вот пример кода:

FestORM.SaleMethod method = new FestORM.SaleMethod
{
    Id = 2,
    Name = "Test Sale Method"
};
FestContext context = new FestContext();

//everything works without this line:
string thisQueryWillMessThingsUp = 
    context.SaleMethods.Where(m => m.Id == 2).Single().Name;

context.Entry(method).State = System.Data.EntityState.Modified;
 context.SaveChanges();

РЕДАКТИРОВАНИЕ для уточнения: Я пытаюсь обновить объект, который уже существует в базе данных.

Все работаетбез запроса, указанного в коде. В моем приложении мой контроллер создает экземпляр контекста, и тот же контекст передается в несколько репозиториев, которые используются контроллером, поэтому я не могу просто использовать другойконтекст для начальной операции запроса.Я пытался удалить сущность из отслеживаемого в ObjectStateManager, но, похоже, я тоже никуда не денусь.Я пытаюсь найти решение, которое будет работать для обоих условий: иногда я буду обновлять объект, который отслеживается ObjectStateManager, а иногда он еще не отслеживается.

FWIW,мои реальные функции хранилища выглядят так, как в коде выше:

public void Update(T entity)
{
    //works ONLY when entity is not tracked by ObjectStateManager
    _context.Entry(entity).State = System.Data.EntityState.Modified; 
}

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

Есть идеи?Я боролся с этим слишком долго ...

Ответы [ 2 ]

12 голосов
/ 08 марта 2012

Проблема в том, что этот запрос

string thisQueryWillMessThingsUp =  
    context.SaleMethods.Where(m => m.Id == 2).Single().Name; 

переносит один экземпляр сущности SaleMethod в контекст, а затем этот код

context.Entry(method).State = System.Data.EntityState.Modified;

присоединяет другой экземпляр к контексту.Оба экземпляра имеют один и тот же первичный ключ, поэтому EF считает, что вы пытаетесь присоединить два разных объекта с одним и тем же ключом к контексту.Он не знает, что они оба должны быть одной и той же сущностью.

Если по какой-то причине вам просто нужно запросить имя, но вы не хотите на самом деле привести полную сущность в контекст,тогда вы можете сделать это:

string thisQueryWillMessThingsUp =           
    context.SaleMethods.Where(m => m.Id == 2).AsNoTracking().Single().Name; 

Если вы пытаетесь обновить существующую сущность, и у вас есть значения для всех сопоставленных свойств этой сущности, то самое простое, что нужно сделать, это не запускатьзапросите и просто используйте:

context.Entry(method).State = System.Data.EntityState.Modified;

Если вы не хотите обновлять все свойства, возможно, из-за того, что у вас нет значений для всех свойств, тогда запросите сущность и задайте для нее свойства перед вызовом SaveChangesэто приемлемый подход.Есть несколько способов сделать это в зависимости от ваших конкретных требований.Одним из способов является использование метода Property, что-то вроде этого:

var salesMethod = context.SaleMethods.Find(2); // Basically equivalent to your query
context.Entry(salesMethod).Property(e => e.Name).CurrentValue = newName;
context.Entry(salesMethod).Property(e => e.SomeOtherProp).CurrentValue = newOtherValue;
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

http://blogs.msdn.com/b/adonet/archive/2011/01/30/using-dbcontext-in-ef-feature-ctp5-part-5-working-with-property-values.aspx

1 голос
/ 08 марта 2012

Очевидным ответом будет то, что вы фактически не сохраняете объект метода в базе данных перед вызовом:

//everything works without this line:
string thisQueryWillMessThingsUp = context.SaleMethods.Where(m => m.Id == 2).Single().Name;

Однако, я думаю, возможно, это всего лишь небольшой код, который вы пропустили.Что если вы сделаете ваши сущности наследующими от абстрактного класса, т. Е.

public abstract class BaseClass
{
     public int Id { get; set; }
}

Затем обновите свой репозиторий до

public class Repository<T> where T : BaseClass
{
 .....
    public void Update(T entity)
    {        
        _context.Entry(entity).State = entity.Id == 0 ? System.Data.EntityState.Added : System.Data.EntityState.Modified; 
    }
}

Также вы можете не устанавливать идентификатор вашего SaleMethod и неэто будет сгенерировано базой данных.Проблема также может быть связана с тем, что объект SaleMethod в базе данных имеет Id 2, а затем вы пытаетесь добавить другой объект SaleMethod с Id 2. Вы видите ошибку, возникающую при попытке добавить другой объект SaleMethod с идентификатором 2 в ObjectStateManager.

...