Итак, у меня есть форма со списком категорий, привязанных к 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 (), но при попытке повторного присоединения при обновлении возникает ошибка: он дублируется, потому что экземпляр этого объекта был создан в форме списка.
Извините за мой плохой английский, надеюсь, вы понимаете!
EDIT:
Теперь я попытался установить состояние сущности категории на Detached сразу после его получения с помощью идентификатора в конструкторе FrmCategoryView
, используя:
dbContext.Entry(category).State = EntityState.Detached;
Это не работает при первом закрытии формы просмотра: строка сетки обновляется, потому что контекст обновляется, даже если для записи category
установлено значение Detached. Но в следующий раз это не произойдет. Это работает так, как должно работать. Изменения, которые я делаю в category
, не отслеживаются контекстом, пока я не присоединю его снова и не сохраню изменения при нажатии кнопки Обновить.