C# Winforms:禁用对绑定 EF 对象的跟踪,以避免在按下"Update"之前进行上下文更新



因此,我有一个表单,其中包含绑定到DataGridView的类别列表,当我双击其中一行时,会打开一个包含类别详细信息的新表单,并且还允许用户编辑或删除所选类别。

这是两种形式的代码:

FrmCategoryList.cs

public partial class FrmCategoryList : Form
{
private readonly DbContext dbContext;
public FrmCategoryList()
{
InitializeComponent();
dbContext = new DbContext();
}
private void FrmCategoryList_Load(object sender, EventArgs e)
{
FillGrid();
}
private void txtSearch_TextChanged(object sender, EventArgs e)
{
FillGrid();
}
private void dgvCategories_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
var grid = (DataGridView)sender;
if (e.RowIndex >= 0)
{
var category = (Category)grid.Rows[e.RowIndex].DataBoundItem;
var frmCategoryView = new FrmCategoryView(category.Id, dbContext);
if (frmCategoryView.ShowDialog() == DialogResult.OK)
FillGrid();
}
}
private void btnNew_Click(object sender, EventArgs e)
{
var frmCategoryView = new FrmCategoryView(0, dbContext);
if (frmCategoryView.ShowDialog() == DialogResult.OK)
FillGrid();
}
private void btnClose_Click(object sender, EventArgs e)
{
Close();
}
private void FillGrid()
{
categoryBinding.DataSource = dbContext.Categories.Where(p => p.Description.StartsWith(txtSearch.Text)).ToList();
}
private void FrmCategoryList_FormClosing(object sender, FormClosingEventArgs e)
{
dbContext?.Dispose();
}
}

FrmCategoryView.cs

public partial class FrmCategoryView : Form
{
private readonly DbContext dbContext;
private Category category;
public FrmCategoryView(int categoryId, DbContext dbContext)
{
InitializeComponent();
this.dbContext = dbContext;
if (categoryId == 0)
{
category = new Category();
btnSave.Visible = true;
}
else
{
category = dbContexto.Categories.Include(c => c.Products).FirstOrDefault(c => c.Id == categoryId);
btnUpdate.Visible = true;
btnDelete.Visible = true;
}
}
private void FrmCategoryView_Load(object sender, EventArgs e)
{
txtDescription.DataBindings.Add("Text", category, "Description");
}
private void btnSave_Click(object sender, EventArgs e)
{
if (category.Validate(dbContext))
{
dbContext.Categories.Add(category);
dbContext.SaveChanges();
MessageBox.Show("Category added!", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
DialogResult = DialogResult.OK;
Close();
}
}
private void btnUpdate_Click(object sender, EventArgs e)
{
if (category.Validate(dbContext))
{
dbContext.Categories.Attach(category);
dbContext.Entry(category).State = EntityState.Modified;
dbContext.SaveChanges();
MessageBox.Show("Category updated!", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
DialogResult = DialogResult.OK;
Close();
}
}
private void btnDelete_Click(object sender, EventArgs e)
{
if (MessageBox.Show("¿Are you sure to delete this category?", "Confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
{
if (category.Products.Count > 0)
{
MessageBox.Show("You cannot delete a category with dependencies.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
dbContext.Categories.Remove(category);
dbContext.SaveChanges();
MessageBox.Show("Category deleted!.", "Info", MessageBoxButtons.OK, MessageBoxIcon.Information);
DialogResult = DialogResult.OK;
Close();
}
}
private void btnClose_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.Cancel;
Close();
}
}

因此,当我编辑某个类别的某个字段,然后在不按"更新"按钮的情况下关闭表单时,问题就来了。直到FrmCategoryList关闭,并且FrmCategoryView中的类别对象绑定到文本,上下文才会被释放,因此上下文会被更新,但更改不会保存到数据库中。因此,如果我打开其他类别并实际更新它,那么在调用SaveChanges()时也会保存以前的更改,因为上下文会跟踪用户"放弃"的更改。

我该怎么办才能解决这个问题?我可以在FrmCategoryView中重新初始化上下文,但如果我真的保存了一些东西,那么更改将不会出现在FrmCategoryList上下文中,而且我不知道如何强制EF检查数据库,即使是已经跟踪的对象。

此外,也许还有一些方法可以将对象从上下文中分离,然后在按下"更新"按钮时重新附加它。我尝试过使用AsNoTracking((,但在更新时尝试重新附加时发生了错误:它是重复的,因为该对象的实例是在列表形式中创建的。

对不起,我的英语不好,希望你能理解!

编辑:

现在,我尝试使用FrmCategoryView构造函数中的Id将类别的Entity State设置为Detached,并使用:

dbContext.Entry(category).State = EntityState.Detached;

当我第一次关闭视图窗体时,它不起作用:网格行会更新,因为上下文会更新,即使category条目设置为Detached也是如此。但接下来的一次,它没有发生。它像应该工作的那样工作。我对category所做的更改不会被上下文跟踪,直到我重新附加它并将更改保存在"更新"按钮上。

这里的问题是您使用了ShowDialog。阅读下面链接备注部分的第二点

https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.form.close?view=netframework-4.7.2

在这种情况下,表单不会被处理。

您已经使用ShowDialog显示了表单。在这些情况下,您将需要手动调用Dispose来标记表单的所有控件垃圾收集。

或者,如果在关闭父窗体之前其他控件没有问题,则根据您想要的模式对话框,通过处理closing事件来清理上下文。

我找到了一个解决方案。

概括一下,这是我对原始代码所做的更改:

  • FrmCategoryList中填充网格时添加了AsNoTracking()
  • 使用FrmCategoryView构造函数中的id获取category后,将其从上下文中分离
  • 在"更新"one_answers"删除"上保存更改之前,请重新附加category,状态分别为"已修改"one_answers"已删除">

经过一些测试,我没有发现任何问题。欢迎提出任何建议。

最新更新