Замените объект в контексте другим экземпляром того же объекта

У меня есть объект, который не связан с моим dbcontext. Я хочу изменить это. Однако к dbcontext уже прикреплен другой экземпляр того же объекта. Если я просто добавлю свою новую сущность, я получаю ошибку, что существо с одинаковым первичным ключом уже прикреплена.
я попробовал несколько разных вариантов удаления старого объекта от dbcontext без каких-либо успехов. Как я могу заменить старый экземпляр новым?
Примечание. Я не хочу копировать значения, я хочу прикрепить этот самый экземпляр моего нового объекта на dbcontext.

var entity = new MyEntity { Id = 1 };
var logicalDuplicate = dbcontext.Set<MyEntity >().Local
    .FirstOrDefault(e => e.Equals(entity));
if (logicalDuplicate != null)
{
    // remove logicalDuplicate from dbcontext
}
dbcontext.MyEntity.Attach(entity);

Для пояснения: я переопределил Equals, чтобы проверить на Id вместо ссылки.


person Tim Pohlmann    schedule 02.06.2016    source источник
comment
Вы пробовали использовать метод обновления? dbcontext.Refresh(RefreshMode.ClientWins, entity)   -  person Florian Haider    schedule 02.06.2016
comment
@FlorianHaider dbcontext не имеет обновления. Но даже если, я не думаю, что это решит проблему.   -  person Tim Pohlmann    schedule 02.06.2016
comment
Моя ошибка, вы должны привести его к ((IObjectContextAdapter)dbcontext).ObjectContext.   -  person Florian Haider    schedule 02.06.2016
comment
@FlorianHaider Я уточнил свой вопрос: я не хочу обновлять свойства. Я хочу прикрепить этот самый экземпляр моей сущности к dbcontext. Refresh на самом деле не помогает мне в этом.   -  person Tim Pohlmann    schedule 02.06.2016
comment
Хорошо, в этом случае просто позвоните ((IObjectContextAdapter)dbcontext).ObjectContext.Detach(logicalDuplicate). Тогда присоединение должно работать в любом случае.   -  person Florian Haider    schedule 02.06.2016
comment
@FlorianHaider Я так и думал... но это не так.   -  person Tim Pohlmann    schedule 02.06.2016
comment
Вы уверены, что это все та же проблема? Я просто попробовал этот код в тесте подразделения, и он работает Fine codepile.net/pile/vjgxmqee   -  person Florian Haider    schedule 02.06.2016
comment
@Florianhaider Я просто понял это: проблема возникает, потому что моя сущность связана с другими объектами, которые все еще прикреплены к контексту. Знаете ли вы простым способом рекурсивно отключить все связанные сущности?   -  person Tim Pohlmann    schedule 06.06.2016
comment
Насколько я знаю, простого пути нет. Вы можете либо копаться в размышлениях, чтобы получить список свойств навигации и отсоединить их, либо посмотреть на ObjectContext -> ObjectStateManager -> GetRelationshipManager() и пройти через связанные цели, не уверены в том, что, как я не пробовал сам.   -  person Florian Haider    schedule 06.06.2016
comment
@FlorianHaider Хорошо, спасибо за помощь.   -  person Tim Pohlmann    schedule 06.06.2016


Ответы (3)


Попробуй это:

if (logicalDuplicate != null)
{
    dbcontext.Entry(logicalDuplicate).State = EntityState.Detached;
    dbcontext.MyEntity.Attach(entity);
    dbcontext.Entry(entity).State = EntityState.Modified;
}
else
{
    dbcontext.MyEntity.Add(entity);
}

Как получить связанные записи

Я исследовал это и хочу поделиться своими результатами. Я использовал отражение как короткий способ получить имена свойств объекта. Но это можно получить без размышлений, как уже упоминал @Florian Haider. Вы можете использовать Ответ и это .

// Found loaded related entries that can be detached later.
private HashSet<DbEntityEntry> relatedEntries;

private DbContext context;

private List<string> GetPropertiesNames(object classObject)
{
    // TODO Use cache for that.
    // From question https://stackoverflow.com/questions/5851274/how-to-get-all-names-of-properties-in-an-entity
    var properties = classObject.GetType().GetProperties(BindingFlags.DeclaredOnly |
                                                              BindingFlags.Public |
                                                              BindingFlags.Instance);
    return properties.Select(t => t.Name).ToList();
}

private void GetRelatedEntriesStart(DbEntityEntry startEntry)
{
    relatedEntries = new HashSet<DbEntityEntry>();

    // To not process start entry twice.
    relatedEntries.Add(startEntry);
    GetRelatedEntries(startEntry);
}

private void GetRelatedEntries(DbEntityEntry entry)
{
    IEnumerable<string> propertyNames = GetPropertiesNames(entry.Entity);
    foreach (string propertyName in propertyNames)
    {
        DbMemberEntry dbMemberEntry = entry.Member(propertyName);
        DbReferenceEntry dbReferenceEntry = dbMemberEntry as DbReferenceEntry;
        if (dbReferenceEntry != null)
        {
            if (!dbReferenceEntry.IsLoaded)
            {
                continue;
            }

            DbEntityEntry refEntry = context.Entry(dbReferenceEntry.CurrentValue);
            CheckReferenceEntry(refEntry);
        }
        else
        {
            DbCollectionEntry dbCollectionEntry = dbMemberEntry as DbCollectionEntry;
            if (dbCollectionEntry != null && dbCollectionEntry.IsLoaded)
            {
                foreach (object entity in (ICollection)dbCollectionEntry.CurrentValue)
                {
                    DbEntityEntry refEntry = context.Entry(entity);
                    CheckReferenceEntry(refEntry);
                }
            }
        }
    }
}

private void CheckReferenceEntry(DbEntityEntry refEntry)
{
    // Add refEntry.State check here for your need.
    if (!relatedEntries.Contains(refEntry))
    {
        relatedEntries.Add(refEntry);
        GetRelatedEntries(refEntry);
    }
}
person Vitaliy Smolyakov    schedule 02.06.2016
comment
Странно, у меня работало - все свойства обновлялись при сохранении. Другой способ — скопировать все свойства в загруженный объект. Для этого вы можете использовать AutoMapper. - person Vitaliy Smolyakov; 02.06.2016
comment
Я говорю не об обновлении свойств, а о присоединении этого самого экземпляра к dbcontext. Должно было быть более ясно, отредактирую вопрос позже. - person Tim Pohlmann; 02.06.2016
comment
Хорошо, я должен исправить себя. Кажется, ваш подход работает. Моя проблема в том, что моя сущность связана с другими сущностями, которые все еще привязаны к контексту. Знаете ли вы простой способ рекурсивно отсоединить все связанные объекты? - person Tim Pohlmann; 06.06.2016
comment
@tim-pohlmann Я добавил в пример кода ответа, как получить связанные записи. - person Vitaliy Smolyakov; 08.06.2016

Редактировать Это находит оригинальный продукт, удаляет его и добавляет новый:

    static void UpdateDatabase() 
    {
        Context context = new Context();
        Product product1 = context.Products.Find(1);
        context.Products.Remove(product1);
        Product product2 = new Product(){ProductId = 1, Name = "Product2"};
        context.Products.Add(product2);
        context.SaveChanges();
    }
person bwyn    schedule 02.06.2016
comment
Я пробовал context.MyEntity.Remove(logicalDuplicate); context.MyEntity.Attach(entity);, и это не работает. - person Tim Pohlmann; 02.06.2016
comment
Вы пробовали context.MyEntity.Add(entity); ? - person bwyn; 02.06.2016
comment
Я попробовал добавить, и поначалу кажется, что это работает, но по какой-то причине в каком-то особом случае ленивая загрузка не работает ... Я думаю, это может быть связано с другой проблемой, в любом случае спасибо за вашу помощь. - person Tim Pohlmann; 06.06.2016

Лучший способ решить эту проблему


DB - это мой объект базы данных
UpdatePrice - это мой объект объекта базы данных
- мой старый объект объекта базы данных

db.Entry(updateprice).CurrentValues.SetValues(ep);

person Akshay Bobade    schedule 28.04.2017
comment
Цитирую вопрос: я не хочу копировать значения, я хочу прикрепить этот самый экземпляр моей новой сущности к dbcontext. - person Tim Pohlmann; 04.05.2017