C# DataGrid绑定和将新行保存到数据库中的问题
我正在使用SQL Server Compact和Entity Framework 6开发一个简单的windows应用程序,我有一个由数据库填充的TreeView,还有一个DataGrid,它应该根据TreeView中选择的节点填充。看起来像这样 数据库模型:C# DataGrid绑定和将新行保存到数据库中的问题,c#,winforms,entity-framework,datagridview,ado.net,C#,Winforms,Entity Framework,Datagridview,Ado.net,我正在使用SQL Server Compact和Entity Framework 6开发一个简单的windows应用程序,我有一个由数据库填充的TreeView,还有一个DataGrid,它应该根据TreeView中选择的节点填充。看起来像这样 数据库模型: 类别 类别 类别名称 子类别 苏比德 子名称 类别 项目 项目ID 项目名称 苏比德 我用这段代码填充了DataGrid,一切都很好,但它不会将新行保存到数据库中,尽管编辑现有行是可行的 private void tree
- 类别
- 类别
- 类别名称
- 子类别
- 苏比德
- 子名称
- 类别
- 项目
- 项目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中选择一个节点时,它应该只显示链接到所选节点的项,但它不会。它将新的查询结果添加到旧的查询结果,在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中添加新行时,它是否可以自动将项目中的子ID字段设置为选定节点的ID,将此新添加的项目链接到树视图中选定的子类别
如果有任何帮助,我将不胜感激,如果解决方案显而易见,我也会感到抱歉,但所有这些对我来说都是新鲜事。谢谢。我对您使用的框架不是很熟悉,但我想尝试一下: 您的第一个代码块具有它的行为,因为
ToList
对符合条件的当前行集进行快照。当您添加新行时,您正在向该快照添加一个项目,但不涉及从中检索其他行的数据库
第二个代码块具有它的行为,因为上下文跟踪本地对象的缓存。调用的第一行只是将这些项带到缓存中,但是以前在缓存中的对象作为本地对象保留在那里
据我所知,这将是最好的解决方案:
BindingSource
设置为的。将BindingSource
设置为context.Items.Local。(
)。现在,BindingSource
充当缓存项的代理BindingSource
的属性。(文档建议Filter
的语法应该与它看起来像SQL的语法相同,因此我打赌有某种方法可以将Linq查询转换为适当的字符串。)总体效果是您将使用来自底层数据库的数据。选择类别后,它将加载相应的实体。然后,它将筛选出它所知道的所有实体的列表,这些实体只是感兴趣的实体。对于选择另一个节点时出现的先前加载的项目,您应该在
treeView\u 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
的Local
集合包含上下文已加载且未删除的所有项目。因此,在每个Load
语句中,您都会不断向该集合添加项。通过将它们的状态更改为分离
,上下文不再“知道”它们的存在,它们从本地
集合中消失
请注意,您不能这样做
context.Items.Local.Clear();
因为除了从Local
集合中删除它们之外,这将标记所有要删除的项<代码>保存更改将从数据库中删除项目
至于向新的子类别添加新项目,最好将这些项目添加到
子类别
中的项目
集合中。这样做,您不需要外键值(此时不知道),但在调用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首先保存子类别
,读取其生成的键值,设置项
的外键值并保存项
,所有这些都在一个事务中完成
如果只是为现有的
子类别创建一个新项目
,则可以直接设置item.SubId
。但是,如果将subcat
附加到上下文,则仍然可以使用subcat.Items.Add(item)
。没错,是否可以在每次选择之前卸载对象?我读了一些书,也许这是唯一的方法,但这对我来说不是很好。我尝试了你的建议,不幸的是它对我不起作用,也许BindingSourceFilter不是这样做的方法。Thanks@Mody当前位置我真的不知道我在做什么,所以我对你的问题悬赏了。希望我们能从知道他们在做什么的人那里得到答案。@icktoofay多么慷慨啊!谢谢。非常感谢,但是当我从TreeView集合中选择另一个子类别时,我在Foreach行上遇到了这个错误;枚举操作可能无法执行。
我尝试了context.Items.Local.Clear()是的,它不起作用。可能忘记了一个ToList()
。你是个救命恩人,谢谢。它工作得很好。如果你能解释一下代码,那就太好了