因此,我有一个带有绑定到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(),但尝试在Update上重新附加时发生错误:它是重复的,因为该对象的实例是以列表形式创建的。
对不起,我英语不好,希望您能理解!
编辑:
现在,在使用FrmCategoryView
构造函数中的ID来获取类别之后,我尝试将类别的实体状态设置为“分离”,
dbContext.Entry(category).State = EntityState.Detached;
我第一次关闭视图窗体时不起作用:网格行由于上下文更新而更新,即使category
条目设置为“分离”也是如此。但是下一次,它没有发生。它像应该工作一样工作。在重新附加并保存“更新”按钮上的更改之前,上下文不会跟踪我对category
所做的更改。
答案 0 :(得分:1)
这里的问题是您使用过ShowDialog
。阅读下面链接的备注部分的第二点
https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.form.close?view=netframework-4.7.2
在这种情况下,表单不会被处理。
您已经使用ShowDialog显示了表单。在这些情况下,您将 需要手动调用Dispose来标记表单的所有控件 垃圾收集。
或者,如果需要模态对话框,请通过处理closing
事件来清理上下文,如果在关闭父窗体之前其他控件仍然没有问题的话。
答案 1 :(得分:0)
我找到了解决方法。
回顾一下,这是我对原始代码所做的更改:
AsNoTracking()
中填充网格时添加了FrmCategoryList
。category
构造函数中的ID从上下文中分离FrmCategoryView
。category
,然后将更改保存在“更新”和“删除”上,并分别具有“已修改”和“已删除”状态。经过一些测试,我没有发现任何问题。任何建议表示赞赏。