我在后台工作人员中添加了一个新对象,该新对象已成功添加,但是如果尝试删除它,则由于新对象与数据上下文分离而出现了错误。
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();
}
我的问题是为什么新对象未附加到数据上下文?我应该在后台工作程序中添加所有新对象吗?
谢谢
我强烈建议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场景中也可能发生。)