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

时间:2014-03-15 09:12:52

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

我正在使用带有Entity Framework 6的SQL Server Compact创建一个简单的Windows应用程序,我有一个由数据库填充的TreeView,还有一个DataGrid应该根据TreeView中的选定节点填充。它看起来像Image

数据库模型:

  • 分类
    • 类别ID
    • 类别名称
  • 子类
    • 子ID
    • SUBNAME
    • 类别ID
    • 的ItemID
    • ITEMNAME
    • 子ID

我使用这段代码来填充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中选择一个节点时,它应该只显示链接到所选节点的 Items ,但它不会将新的查询结果添加到旧的,如果我选择了更多的节点,那么在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中添加新行时,它可以自动将中的 SubID 字段设置为所选节点的ID,这个新添加的到TreeView中选定的子类别

任何帮助都会受到赞赏,如果解决方案很明显,我很抱歉,但所有这些对我来说都是新的。感谢。

2 个答案:

答案 0 :(得分:2)

对于在选择其他节点时出现的先前加载的项目,您应该在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集合中有效删除。 Local的{​​{1}}集合包含上下文已加载但尚未删除的所有项目。因此,对于每个DbSet<T>语句,您都要继续向此集合添加项目。通过将状态更改为Load,上下文不再“知道”#34;关于他们的存在,他们从Detached集合中消失。

请注意,您无法

Local

因为除了将其从context.Items.Local.Clear(); 集合中删除之外,它还会标记要删除的所有项目。 Local将从数据库中删除项目。


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

看起来像

SaveChanges

现在,EF首先保存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(); ,读取其生成的键值,设置subcat的外键值,并将item保存在一个事务中。

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

答案 1 :(得分:1)

我对你正在使用的框架不是很熟悉,但我会对它进行一次尝试:

您的第一个代码块具有它所执行的行为,因为ToList会获取与条件匹配的当前行集的快照。添加新行时,您将向该快照添加项目,但不会触及从中检索其他行的数据库。

第二个代码块具有它所执行的行为,因为上下文跟踪本地对象的缓存。调用Load的第一行只是将这些项放入缓存中,但之前位于缓存中的对象将作为本地对象保留在那里。

从我有限的知识来看,这将是最好的解决方案:

  1. 首次创建表单时,请创建BindingSource。将此BindingSource设置为DataGridDataSource。将BindingSource的{​​{3}}设置为context.Items.Local.DataSource()。现在BindingSource充当缓存项的代理。

  2. 选择某个项目时,ToBindingList正在执行您正在执行的相应实体,但也会相应地设置BindingSource的{​​{3}}。 (文档建议Filter的语法应该与Load接受相同 - 它看起来像SQL一样,所以我打赌有一些方法可以将Linq查询转换为适当的字符串。)

  3. 总体效果是您将使用底层数据库中的数据。选择类别后,它将加载适当的实体。然后它会过滤掉它所知道的所有实体的列表。