我正在使用带有Entity Framework 6的SQL Server Compact创建一个简单的Windows应用程序,我有一个由数据库填充的TreeView,还有一个DataGrid应该根据TreeView中的选定节点填充。它看起来像Image
数据库模型:
我使用这段代码来填充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中选定的子类别。
任何帮助都会受到赞赏,如果解决方案很明显,我很抱歉,但所有这些对我来说都是新的。感谢。
答案 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
的第一行只是将这些项放入缓存中,但之前位于缓存中的对象将作为本地对象保留在那里。
从我有限的知识来看,这将是最好的解决方案:
首次创建表单时,请创建BindingSource
。将此BindingSource
设置为DataGrid
的DataSource
。将BindingSource
的{{3}}设置为context.Items.Local.
。现在DataSource
()BindingSource
充当缓存项的代理。
选择某个项目时,ToBindingList正在执行您正在执行的相应实体,但也会相应地设置BindingSource
的{{3}}。 (文档建议Filter
的语法应该与Load
接受相同 - 它看起来像SQL一样,所以我打赌有一些方法可以将Linq查询转换为适当的字符串。)
总体效果是您将使用底层数据库中的数据。选择类别后,它将加载适当的实体。然后它会过滤掉它所知道的所有实体的列表。