Можно ли остановить Entity Framework, исправляя отношения после SaveChanges?

Я получаю это исключение из Entity Framework 4.3.1 (кстати, из веб-приложения ASP .NET MVC 3, созданного в Visual Studio 2010 против .NET 4):

System.InvalidOperationException: изменения в базе данных были успешно зафиксированы, но при обновлении контекста объекта произошла ошибка. ObjectContext может находиться в несогласованном состоянии. Внутреннее сообщение об исключении: объект не может быть добавлен в контекст объекта. EntityKey объекта имеет ObjectStateEntry, указывающий, что объект уже участвует в другом отношении.

Я немного погуглил и пришел к этому (на msdn):

InvalidOperationException при сохранении

В этом Джефф Дерстадт (принятый ответ) говорит:

Когда вы затем вызываете SaveChanges, контекст сохраняет изменения, а затем пытается исправить записи состояния. Когда он это делает, он выполняет «исправление ключа», при котором временные ключи EntityKey преобразуются в полные ключи EntityKey. В этом случае временный ключ TKP2 теперь будет равен ключу для P2. Контекст пытается «повысить» ключевую запись в диспетчере состояний до полной записи сущности, и в процессе исправляет граф. Вот где возникает исключение из-за конфликта: P1.Spouse = P2, поэтому P2.Spouse должен = P1, но поскольку были добавлены элементы, P2.Spouse уже равен P3, а EntityReference не может иметь более одного значения. К сожалению, этот случай не всегда можно обнаружить до того, как произойдет сохранение базы данных, потому что временные EntityKeys не эквивалентны полным EntityKeys, и поэтому мы не можем идентифицировать будущие коллизии, подобные этому.

Я думаю здесь немного «грязно», поэтому мой вопрос таков...

Поскольку мне все равно, как выглядит граф объектов после сохранения изменений (это конец моего запроса, поэтому он все равно выбрасывается), и данные были вставлены (и на самом деле все в порядке, когда я смотрю на это в SQL Server после того, как я позволю ему продолжить работу в отладчике) - есть ли способ сказать Entity Framework, чтобы он не делал этот шаг (и, следовательно, не получал это исключение и, по-видимому, также экономил немного времени)?


person kmp    schedule 04.10.2012    source источник
comment
Когда я попробовал Entity Framework, у меня было много проблем, когда я пытался сделать что-то немного нестандартное. Я нашел NHibernate намного проще в использовании.   -  person Oliver    schedule 04.10.2012


Ответы (2)


Вы должны опубликовать немного кода, если вам нужна лучшая помощь.

Тем не менее, я обнаружил, что борюсь с некоторыми из этих ошибок и пришел к основной истине: если вы ссылаетесь на что-то, что уже существует в базе данных, ссылайтесь на это. Не используйте просто ключи или коды, используйте реальные объекты, представленные в ваших DbContexts. Вы можете сделать это, найдя их с помощью метода DbContext.Entry().

С другой стороны, если и исходный, и объект, на который ссылаются, создаются во время выполнения, убедитесь, что им присвоен уникальный идентификатор, и этот идентификатор известен инфраструктуре Entity (а не базе данных) во время выполнения. В противном случае (например, если вы полагаетесь на функцию NEWID() Sql) объект A может ссылаться на объект B с идентификатором времени выполнения, который не равен идентификатору, который тот же объект получает после сохранения в базе данных.

Как я уже сказал, если вы опубликуете немного кода, мы могли бы помочь лучше;)

person Isaac Llopis    schedule 10.12.2012

нет официального способа сделать это, но попробуйте следующее:

private static readonly Lazy<MethodInfo> internalDeleteMethod = new Lazy<MethodInfo>(
    () => typeof(ObjectContext).Assembly.GetType("System.Data.Objects.EntityEntry").GetMethod("Delete", BindingFlags.Instance | BindingFlags.NonPublic, null, new[] { typeof(bool) }, null)); 


public static void Delete<TEntity>(Func<DbContext> dbContextFactory, TEntity entity,
    Func<DbContext, IEnumerable<DbEntityValidationResult>> onSaving = null,
    Action<DbContext> onSaved = null,
    bool referenceFixup = true)
        where TEntity : class
{
    Guard.ArgumentNotNull(dbContextFactory, "dbContextFactory");
    Guard.ArgumentNotNull(entity, "entity");
    onSaving = onSaving ?? (_ => null);
    onSaved = onSaved ?? (_ => { });

    using (var dbContext = dbContextFactory())
    {
        var set = dbContext.Set<TEntity>();
        set.Attach(entity);

        var entry = ((IObjectContextAdapter)dbContext).ObjectContext.ObjectStateManager.GetObjectStateEntry(entity);
        internalDeleteMethod.Value.Invoke(entry, new object[] { referenceFixup });

        dbContext.SaveChanges();
     }
 }
person benwasd    schedule 16.10.2013