C # Winforms: отключить отслеживание связанного объекта EF, чтобы избежать обновления контекста, пока не будет нажата кнопка «Обновить» - PullRequest
0 голосов
/ 09 ноября 2018

Итак, у меня есть форма со списком категорий, привязанных к 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, не отслеживаются контекстом, пока я не присоединю его снова и не сохраню изменения при нажатии кнопки Обновить.

Ответы [ 2 ]

0 голосов
/ 10 ноября 2018

Я нашел решение.

Напомним, что это изменения, которые я внес в исходный код:

  • Добавлено AsNoTracking() при заполнении сетки в FrmCategoryList.
  • Отсоединение category от контекста после получения его с использованием идентификатора в конструкторе FrmCategoryView.
  • Повторно прикрепить category перед сохранением изменений в «Обновлении» и «Удалить» с состояниями «Изменено» и «Удалено» соответственно.

После некоторых тестов я не нашел никаких проблем Любые предложения приветствуются.

0 голосов
/ 09 ноября 2018

Проблема в том, что вы использовали ShowDialog. Прочитайте второй пункт в разделе замечаний ниже по ссылке

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

В этом случае форма не удаляется.

вы отобразили форму, используя ShowDialog. В этих случаях вы будете необходимо вызвать Dispose вручную, чтобы отметить все элементы управления формы для сборка мусора.

Или, как вы хотите, модальное диалоговое окно, очистите контекст, обработав событие closing, если у вас нет проблем с другими элементами управления, живущими до закрытия родительской формы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...