DataGrid绑定和将新行保存到数据库的问题

本文关键字:数据库 问题 保存 新行 绑定 DataGrid | 更新日期: 2023-09-27 17:50:19

我正在使用SQL Server Compact with Entity Framework 6开发一个简单的windows应用程序,我有一个由数据库填充的TreeView,还有一个DataGrid,应该根据TreeView中选择的节点填充。它看起来像这个Image

数据库模型:

    <
  • 类别/strong>
    • CategoryID
    • CategoryName
  • 子目录
    • SubID
    • SubName
    • CategoryID
  • 项目
    • ItemID
    • ItemName
    • SubID

我用这段代码来填充DataGrid,一切都工作得很好,但它不会将新行保存到数据库,编辑存在的行正在工作。

private void treeView_AfterSelect(object sender, TreeViewEventArgs e)
{
    if (treeView.SelectedNode.Level == 1)
    {
        BindingSource bs = new BindingSource();
        bs.DataSource = (from a in context.Items where a.SubID == (int)treeView.SelectedNode.Tag select a).ToList();
        dataGrid.DataSource = bs;
    }
}  

所以,我尝试了这个。保存新行和编辑存在的工作,事情是当选择一个节点在TreeView它应该只显示项目链接到所选节点,但它不添加新的查询结果到旧的,在DataGrid上显示两个查询,可能会更多,如果我选择更多的节点。

private void treeView_AfterSelect(object sender, TreeViewEventArgs e)
{
    if (treeView.SelectedNode.Level == 1)
    {
        context.Items.Where(a => a.SubID == (int)treeView.SelectedNode.Tag).Load();
        dataGrid.DataSource = context.Items.Local.ToBindingList();
    }
} 

还有一件事我想做,当从TreeView中选择子类别时,我应该得到项目链接到这个特定的子类别显示在DataGrid上,所以有可能在DataGrid中添加新行时,它会自动设置 subbid 字段在项目到选定节点的ID,链接这个新增的项目到选定的子类别在TreeView中。

任何帮助将不胜感激,抱歉,如果解决方案是显而易见的,但这一切都是新的我。谢谢。

DataGrid绑定和将新行保存到数据库的问题

对于在选择另一个节点时出现的先前加载的项目,您应该在treeView_AfterSelect:

中添加这一行
foreach(var item in context.Items.Local.ToList())
    context.Entry(item).State = System.Data.Entity.EntityState.Detached;

,然后继续

context.Items.Where(a => a.SubID == (int)treeView.SelectedNode.Tag).Load();

添加的行从变更跟踪器中删除项目,并且它们将有效地从Local集合中删除。DbSet<T>Local集合包含上下文曾经加载且未被删除的所有项。因此,对于每个Load语句,您不断向此集合添加项。通过将它们的状态更改为Detached,上下文不再"知道"它们的存在,并且它们从Local集合中消失。

注意你不能做

context.Items.Local.Clear();

,因为这将标记所有项为删除,除了从Local集合中删除它们。SaveChanges将从数据库中删除条目。


对于将新项目添加到新的子类别,最好的做法是将这些项目添加到SubCategory中的Items集合中。这样做,你不需要一个外键值,这是不知道的时刻,但当调用SaveChanges, EF将首先保存子类别,并设置生成的FK值在项目"及时"。

它看起来像

var subcat = new SubCategory();
var item = new Item();
subcat.Items.Add(item); // subcat.Items must have been initialized! 
context.SubCategories.Add(subcat); // Also marks item as Added
context.SaveChanges();

现在EF首先保存subcat,读取其生成的键值,设置item的外键值并保存item,所有这些都在一次事务中完成。

如果你只是为现有的SubCategory创建一个新项目,你可以直接设置item.SubId。但是,如果subcat附加到上下文,则subcat.Items.Add(item)仍然可以使用。

我对您使用的框架不是很熟悉,但我将尝试一下:

您的第一个代码块具有它所做的行为,因为ToList获取匹配标准的当前行集的快照。当您添加新行时,您是在向该快照添加一个项,但没有触及从其中检索其他行的数据库。

第二段代码之所以有这样的行为,是因为上下文跟踪了本地对象的缓存。调用Load的第一行只是将这些项放入缓存,但是之前在缓存中的对象作为本地对象留在那里。

根据我有限的知识,这将是最好的解决方案:

  1. 第一次创建表单时,创建一个BindingSource。将此BindingSource设置为DataGridDataSource。设置BindingSourceDataSourcecontext.Items.Local.ToBindingList()。现在BindingSource作为缓存项的代理。

  2. 当一个项目被选中,Load适当的实体,因为你现在正在做的,但也设置BindingSourceFilter适当。(文档建议Filter的语法应该与DataColumn.Expression接受的语法相同-它看起来像sql,所以我打赌有一些方法可以将Linq查询转换为适当的字符串。)

总体效果是您将使用来自底层数据库的数据。当选择一个类别时,它将加载适当的实体。然后,它将过滤所有已知实体的列表,只保留感兴趣的实体。