从数据上下文分离了新对象

问题描述 投票:-1回答:1

我在后台工作人员中添加了一个新对象,该新对象已成功添加,但是如果尝试删除它,则由于新对象与数据上下文分离而出现了错误。

Background Worker Do_Work Event

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();
    }

Delete Button(在主线程上),如果我附加了新对象,则可以使用

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();
    }

我的问题是为什么新对象未附加到数据上下文?我应该在后台工作程序中添加所有新对象吗?

谢谢

c# wpf entity-framework backgroundworker
1个回答
0
投票

我强烈建议not使用实体,尤其是控件绑定中的跟踪实体。被跟踪实体的生存期仅应与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)时,请使用Delete按钮,然后检查上下文以查看是否已跟踪该实体,如果没有,请附加该实体(或其代理)并执行删除。如果已经被跟踪,则删除被跟踪的一个:

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不会访问数据库,它只是查找该ID的跟踪实体引用。如果找到一个,我们将其删除。如果没有,我们可以将其附加并删除以发出Delete。

寿命长的DbContext可能会导致您出现问题,因为通过各种操作,可能已经跟踪了具有列表项ID的实体,因此,使用存储在List控件中的陈旧副本的附加/分离状态将不会可靠。

如果列表项返回要删除的实体的ID,而不是未跟踪的实体引用,您仍然可以有效地删除该实体:

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();
}

没什么不同,但是删除了您的列表项实际上包含一个完整实体的假设。列表视图模型也可以比整个实体紧凑得多,所需的内存和加载时间更少。处理附加和分离的实体的风险是,如果已经跟踪了该ID的另一个实体(由于编辑等),并且对于诸如编辑之类的操作,这些实体仍然过时,则仍需要根据上下文评估这些实体。 。其他应用程序/用户/进程可能已经更新了实体背后的数据。通过附加并执行诸如编辑和保存更改的操作,该实体仅保留该用户的最后一个已知状态,该状态可以并且将覆盖其他用户所做的更改。处理并发编辑时,最好加载该实体,检查行的版本或时间戳以查看自上次检索以来是否已对其进行修改,并处理场景是否存在。您还可能遇到以下问题:自您的当前会话检索基础记录以来,该基础记录可能已被删除,在这种情况下,附加,更新和SaveChanges将导致“预期为1,发现为0”错误或类似错误。 (不确定,但是在Delete场景中也可能发生。)

© www.soinside.com 2019 - 2024. All rights reserved.