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中。
任何帮助将不胜感激,抱歉,如果解决方案是显而易见的,但这一切都是新的我。谢谢。
对于在选择另一个节点时出现的先前加载的项目,您应该在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
的第一行只是将这些项放入缓存,但是之前在缓存中的对象作为本地对象留在那里。
根据我有限的知识,这将是最好的解决方案:
第一次创建表单时,创建一个
BindingSource
。将此BindingSource
设置为DataGrid
的DataSource
。设置BindingSource
的DataSource
为context.Items.Local.ToBindingList()
。现在BindingSource
作为缓存项的代理。当一个项目被选中,
Load
适当的实体,因为你现在正在做的,但也设置BindingSource
的Filter
适当。(文档建议Filter
的语法应该与DataColumn.Expression
接受的语法相同-它看起来像sql,所以我打赌有一些方法可以将Linq查询转换为适当的字符串。)
总体效果是您将使用来自底层数据库的数据。当选择一个类别时,它将加载适当的实体。然后,它将过滤所有已知实体的列表,只保留感兴趣的实体。