Новый объект отделен от контекста данных - PullRequest
0 голосов
/ 04 ноября 2019

Я добавил новый объект в фоновом режиме. Новый объект был успешно добавлен, но если попытаться удалить его, я получил ошибку, поскольку новый объект был отсоединен от контекста данных.

Фоновый рабочийСобытие Do_Work

private void _anotherThread_DoWork(object sender, DoWorkEventArgs e)
    {
        testdbEntities context = new testdbEntities();

        _item = new item();
        this.txbItemName.Dispatcher.Invoke(new Action(delegate () { _item.name = this.txbItemName.Text; }));

        context.item.Add(_item);
        Application.Current.Dispatcher.BeginInvoke(new Action(() => this.ocItems.Add(_item)));

        context.SaveChanges();
    }

Кнопка удаления (в главном потоке), если я прикрепил новый объект, он работает

private void btnDelete_Click(object sender, RoutedEventArgs e)
    {
        Item _selectedItem = (Item)lbItems.SelectedItem;

        if (_context.Entry(_selectedItem).State == EntityState.Detached)
            _context.Item.Attach(_selectedItem);

        _context.Item.Remove(_selectedItem);
        _context.SaveChanges();
    }

У меня вопрос, почемуновый объект не привязан к контексту данных? Должен ли я связывать каждый новый объект, добавленный в фоновом режиме?

Спасибо

1 Ответ

1 голос
/ 05 ноября 2019

Я бы настоятельно рекомендовал , а не , используя сущности, особенно отслеживаемые сущности в ваших привязках управления. Отслеживаемый объект должен жить только до тех пор, пока он является DbContext, а любой созданный вами DbContext должен иметь определенную область действия. (Удалите их)

Таким образом, в фоновом задании новый DbContext должен быть ограничен в пределах блока using, чтобы обеспечить его удаление.

private void _anotherThread_DoWork(object sender, DoWorkEventArgs e)
{
    using(testdbEntities context = new testdbEntities())
    {
        _item = new item();
        this.txbItemName.Dispatcher.Invoke(new Action(delegate () { _item.name = this.txbItemName.Text; }));

        context.item.Add(_item);
        Application.Current.Dispatcher.BeginInvoke(new Action(() => this.ocItems.Add(_item)));

        context.SaveChanges();
        context.Detach(_item); 
    }
}

Затем, когда вы перейдете к использованию кнопки «Удалить», если вы имеете дело с долгоживущим DbContext (_context), то проверьте контекст, чтобы увидеть, отслеживается ли этот объект, если нет, то присоедините его. (или прокси к нему) и выполните удаление. Если он уже отслежен, удалите отслеженный:

private void btnDelete_Click(object sender, RoutedEventArgs e)
{
    Item _selectedItem = (Item)lbItems.SelectedItem;

    Item existingItem = _context.Item.Local.SingleOrDefault(x => x.ItemId == _selectedItem.ItemId);
    if (existingItem != null)
        _context.Item.Remove(existingItem);
    else
    {
        _context.Item.Attach(_selectedItem);
        _context.Item.Remove(_selectedItem);
    }
    _context.SaveChanges();
}

Проверка .Local не попадает в базу данных, он просто ищет ссылку на отслеживаемый объект для этого идентификатора. Если кто-то найден, мы удалим это. Если нет, мы можем присоединить наш и удалить его, чтобы выдать команду Delete.

Долгоживущие DbContexts могут вызвать проблемы, потому что с помощью различных операций уже может отслеживаться объект с идентификатором элемента списка, поэтому с помощьюприкрепленное / отсоединенное состояние устаревшей копии, которая была сохранена в элементе управления List, не будет надежным.

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

private void btnDelete_Click(object sender, RoutedEventArgs e)
{
    int itemId = lbItems.SelectedItem.ItemId; // Where a simple view model was put in the SelectedItem, not an entity.

    Item existingItem = _context.Item.Local.SingleOrDefault(x => x.ItemId == itemId);
    if (existingItem != null)
        _context.Item.Remove(existingItem);
    else
    { 
        var tempItem = new Item { ItemId = itemId }; 
        _context.Item.Attach(tempItem);
        _context.Item.Remove(tempItem);
    }
    _context.SaveChanges();
}

Не сильно отличается, но удаляет предположение, чтоВаш элемент списка содержит завершенную сущность, хотя на самом деле это не так. Модель представления списка также может быть намного более компактной, чем вся сущность, что требует меньше памяти и времени для загрузки. Риск работы с присоединенными и отсоединенными объектами заключается в том, что эти объекты по-прежнему необходимо оценивать в контексте, если другой объект для этого идентификатора уже отслежен (из-за изменений и т. Д.), А для таких операций, как изменения, эти объекты могут быть устаревшими. ,Другие приложения / пользователи / процессы могли бы обновить данные за объектом. Добавляя и делая что-то вроде редактирования и сохранения изменений, этот объект сохранял только последнее известное состояние этого пользователя, которое может перезаписывать изменения, сделанные другими пользователями. При работе с параллельными изменениями предпочтительно загружать объект, проверять версию строки или временную метку, чтобы увидеть, был ли он изменен с момента последнего получения, и обработать сценарий, если / где он есть. Вы также можете столкнуться с проблемами, при которых основная запись могла быть удалена с момента ее получения в текущем сеансе, и в этом случае присоединение, обновление и SaveChanges приводили к ошибке «ожидается 1, найдено 0» или аналогичной. (Не уверен, но это может произойти и в сценарии удаления.)

...