【问题标题】:Why SaveChanges() save only modification of existing rows, but not deleted or added entries?为什么 SaveChanges() 只保存对现有行的修改,而不是删除或添加的条目?
【发布时间】:2024-01-08 23:37:06
【问题描述】:

使用; c#, .net 4.5 vs 2012 winforms

尝试使用 EntityFramework 将一些新数据保存到已经绑定到 dataGridview 的数据库中。

要在 dataGridView 中显示来自 db 的数据,请使用以下代码:

所有方法都使用

LibraryLib.LibraryEntities context = new LibraryLib.LibraryEntities();

在一种方法中,我称之为:

BindingSource bs = new BindingSource();
bs.DataSource = typeof(LibraryLib.Book);
context.Book.ToList().ForEach(n => bs.Add(n));
dataGridViewDB.DataSource = bs;

BindingSource bstab2 = new BindingSource();
bstab2.DataSource = typeof(LibraryLib.LibraryStock);
context.LibraryStock.ToList().ForEach(n => bstab2.Add(n));
dataGridViewCategoryDB.DataSource = bstab2;

在另一种方法中,使用下一个代码保存数据(更改后按按钮调用):

context.SaveChanges();
context.Dispose();

我的数据库图表

结果 -

我可以在 dataGridview 中看到我所有的数据库条目。

我可以更改任何单元格,如果保存 - 全部保存。但是,如果我添加新项目或删除现有项目(在两个表中 - 意味着从两个表中删除依赖条目),此更改不会保存? 输入数据时,所有必需的数据格式(平均 varchar 或 int) - 好的。

正如我所读 - 我可以将数据绑定到 datagridView 然后修改它,添加或删除任何条目,然后使用 ObjectSetsaveChanges() 中的方法保存它?我说的对吗?

问题为什么我在保存更新(添加或删除)数据时遇到问题,我错在哪里?


编辑

非常感谢potehin143

的帮助

我用另一种方法解决了这个问题 - 创建一些方法来添加和删除 DatdBase 中的条目

由于我的程序设计视图,我尝试对所有类型的引擎使用通用逻辑(意味着诸如添加、删除、更新库中的条目等功能)

接下来要添加代码:

public void AddNewBookEF()
    {
        string id = Guid.NewGuid().ToString();
        LibraryLib.LibraryStock newCat = new LibraryLib.LibraryStock();
        newCat.ID = id;
        newCat.Category = listBoxShelfDB.Text;
        context.LibraryStock.Add(newCat);

        LibraryLib.Book newBook = new LibraryLib.Book();
        newBook.ID = id;
        newBook.Author = textBoxAuthorDB.Text;
        newBook.Name = textBoxNameDB.Text;
        newBook.Year = Int32.Parse(numericUpDownYearDB.Value.ToString());
        newBook.Genre = textBoxGenreDB.Text;
        context.Book.Add(newBook);
        context.SaveChanges();
    }

对于从数据库中删除条目,请准备如下代码:

public void RemoveFromDBEF(string bookId)
    {
        if (dataGridViewDB.SelectedRows.Count == 1)
        {
            LibraryLib.Book bookToDelete = new LibraryLib.Book();
            bookToDelete.ID = bookId;

            foreach (LibraryLib.Book entry in context.Book)
            {
                if (entry.ID == bookId)
                    context.Book.Remove(entry);
            }
            foreach (LibraryLib.LibraryStock entry in context.LibraryStock)
            {
                if (entry.ID == bookId)
                    context.LibraryStock.Remove(entry);
            }
            context.SaveChanges();
        }
        else
        {
            MessageBox.Show("Please select only one row for deleting in Book table");
        }
    }

bookId - Guid 标识符,转换为字符串并保存到 DB(当我开始准备程序时,我不知道 DB 中现有的 Guid 类型,所以不要为这个程序重写所有代码,我只是现在学习)。要在 DataGridView 中查找所选书籍的 bookId,请使用如下代码:

    private string GetGuidAsString()
    {
        try
        {
    ...
            if (this.tabControlEngines.SelectedIndex == 2)
            {
                //get selected number of cell
                int index = dataGridViewDB.SelectedCells[0].RowIndex;
                //get selected row
                DataGridViewRow selectedBook = dataGridViewDB.Rows[index];
                string guidOfBook = Convert.ToString(selectedBook.Cells[0].Value);
                return guidOfBook;
            }
        }
        catch (ArgumentOutOfRangeException)
        {
            MessageBox.Show("Please select a book or add books to empty lib", "No book selected");
            return null;
        }
        catch (Exception) { }
        return null;
    }

也许这个答案对某些人会有所帮助。

【问题讨论】:

    标签: c# .net winforms entity-framework datagridview


    【解决方案1】:

    您有 2 个不同的位置存储对数据项的引用 上下文和绑定源

    因此,如果您向绑定源添加某些内容,则需要将相同的项目添加到上下文中 和删除一样

    如果您只是通过 DataGridView 将项目添加到 bindingsorce 您的上下文不知道它

    试试这个

    // Here is Example for Book Entity
    
    
     context.Book.ToList().ForEach(n => bs.Add(n)); 
     // attach event after fill data
     bs.ListChanged += bs_ListChanged;
    
    
    void bs_ListChanged(object sender, ListChangedEventArgs e)
        {
            switch (e.ListChangedType)
            {
                case ListChangedType.ItemAdded:
                    context.Book.Add((Book)((BindingSource)sender).List[e.NewIndex]); // Adding to Navigating Collection 
                    break;
    
                case ListChangedType.ItemDeleted:
                    contextBook.Remove((Book)((BindingSource)sender).List[e.OldIndex]);
                    break;
            }
    
        }
    
    
    //detaching events for reloading data 
    bs.ListChanged -= bs_ListChanged;
    // here reloading data from dbase
    bs.ListChanged += bs_ListChanged;
    

    实体之间的关系看起来是一对一的,因此您可能需要另一种方式来 通过像这样的连接类添加和删除两个实体的数据

    public class JoinClass
    {
        private Book _Book;
        private LibraryStock _LibraryStock;
        public Book GetBook() { return _Book; }
        public LibraryStock GetLibraryStock() { return _LibraryStock; }
    
        public JoinClass()
        {
            _LibraryStock = new LibraryStock();
            _Book = new Book();
            _LibraryStock.Book = _Book;
        }
        public JoinClass(LibraryStock libraryStock)
        {
            _LibraryStock = libraryStock;
            _Book = libraryStock.Book;
        }
    
     // here properties of both entities
    
    }
    

    然后你可以使用一个 BindingSorce 和一个事件处理程序

    【讨论】:

    • 我有一个问题 - 我是否需要将此事件添加到两个表(由于可能会更改两个表)如果是 - 尝试将此事件添加到两个绑定源。结果 - 如果在我添加或删除项目之前 - 上下文是相同的 - 现在它正在更新添加的项目(在两个表中)但是,如果我尝试保存它 - 有异常 - DUpdateException。
    • 另外,如果我将 anly 添加到一个表数据 - 上下文添加 1 个实体,但如果 oto 两个表(首先创建一些键值,然后在另一个表中创建一些具有此键值的书)上下文变得像 context+ =上下文-意味着如果我在更改后有 2 个条目,我将有 4 个条目。再次 - 在保存期间有 DBUpdate 异常。
    • 还有另一个问题-为什么要从现有数据库中更新任何单元格,我可以在没有任何事件的情况下执行此操作,但要添加或删除条目,我需要使用事件?为什么会这样?
    • 你们介意在此处保留此讨论的任何结果吗,也许在编辑后的答案中?
    • Gerd,是的,我们肯定会公布所有结果。问题是为什么视觉集合更改对上下文集合没有影响。答案是它需要处理 BindingSource 集合的更改和在 DbContext 中添加(删除)新(旧)项。麻烦的原因在于如何正确地将实体项添加到上下文中。这是另一个话题,但我已要求作者将实体类的详细信息发送给我,以便我自己调试。很快我们将发布编辑过的问题和编辑过的答案。
    最近更新 更多