防止Entity Framework重复插入关联对象
Entity Framework在数据库与对象映射上做了很多工作,除了将数据库里的表映射成相应的对象以外,它还能够自动处理表之间的外键关系,并且可以用导航属性(Navigation Property)的方式在对象层面上表示这些关系。
一般来说,当你插入一个对象时,Entity Framework默认会自动将对象通过导航属性关联的对象也插入到数据库里面去,大部分情况下,这是我们想要的结果。当然,如果关联的对象已经存在于数据库当中时,Entity Framework会避免重复插入对象。但问题是,这个检查对象已经存在避免重复插入数据的功能,好像只在一个Context(环境)下有效,即下面的代码是可以正常执行的:
using (var context = new TestContext())
{
var milestone = new Milestone()
{
Title = "测试里程碑",
StartDate = DateTime.Now,
DueDate = DateTime.Now + TimeSpan.FromDays(30)
};
context.Milestones.Add(milestone);
context.SaveChanges();
id = milestone.Id;
var project = new Project()
{
Title = "测试项目",
StartDate = DateTime.Now,
DueDate = DateTime.Now + TimeSpan.FromDays(30),
Owner = "测试用户"
};
project.Children.Add(milestone);
context.Project.Add(project);
context.SaveChanges();
}
而如果对象是跨Context(环境)的话,或者基于现有对象复制的对象(包括主键也复制的情况),这就会产生重复插入的问题,因为新复制的对象,Entity Framework没有办法跟踪对象的状态,“误以为”对象是一个全新的对象,比如,下面这段代码就会导致Entity Framework抛出一个异常,异常根据Entity对象的数据库约束不同,可能会报告不同的错误信息—这个问题一开始让我迷惑了好几天:
public static void Main(string[] args)
{
int id = 0;
using (var context = new TestContext())
{
var milestone = new Milestone()
{
Title = "测试里程碑",
StartDate = DateTime.Now,
DueDate = DateTime.Now + TimeSpan.FromDays(30)
};
context.Milestones.Add(milestone);
context.SaveChanges();
id = milestone.Id;
}
using ( var context = new TestContext())
{
var project = new Project()
{
Title = "测试项目",
StartDate = DateTime.Now,
DueDate = DateTime.Now + TimeSpan.FromDays(30),
Owner = "测试用户"
};
var child = new Milestone() {
Id = id
};
project.Children.Add(child);
context.Project.Add(project);
context.SaveChanges();
}
}
执行上面这段代码,Entity Framework会在最后一个context.SaveChanges()上面抛出DbUpdateException,详细信息是:“{"The conversion of a datetime2 data type to a datetime data type resulted in an out-of-range value.\r\nThe statement has been terminated."}”。这个异常一开始看上去太怪异了, 明明我将要保存的Project对象的所有DateTime类型都已经赋值(而且赋值都在范围内)了,为什么还说超出赋值范围呢?
后面才发现,这是因为,Entity Framework在插入project对象是,看到它的关联对象列表Children里,有一个Milestone对象,而Milestone对象是重新复制的(只复制了ID)—这个场景是因为用户在网页上创建一个项目时,可以从里程碑列表里选择一个事先创建好了的里程碑。由于Entity Framework没有办法跟踪这个新复制的Milestone对象的状态,所以它“误认为”这个对象是一个新的对象,因此重新插入这个对象,而这个对象又没有设置一些必要的日期属性,导致了前面那个异常。
既然搞明白了道理,修复起来也很简单,就是显式告诉Entity Framework跟踪这个对象—通过把第二个using段改成下面这样:
using ( var context = new TestContext())
{
var project = new Project()
{
Title = "测试项目",
StartDate = DateTime.Now,
DueDate = DateTime.Now + TimeSpan.FromDays(30),
Owner = "测试用户"
};
var child = new Milestone() {
Id = id
};
project.Children.Add(child);
var adapter = context as IObjectContextAdapter;
adapter.ObjectContext.AttachTo("Milestones", child);
context.Project.Add(project);
context.SaveChanges();
}
注意:我用的是Entity Framework CTP 5,采用的是代码优先(code first)的方式创建的数据库,但是本文提到的问题在数据库优先和模型优先的情况里都是一样的。
因为在网上找了好多文章都没有提到这个问题,所以在这里记录下来。
重现代码:/Files/killmyday/codefirstef.zip
转载于:https://www.cnblogs.com/killmyday/archive/2010/12/17/1909630.html
防止Entity Framework重复插入关联对象相关推荐
- Entity Framework 批量插入
为什么80%的码农都做不了架构师?>>> 奋斗的小鸟--dogxuefeng Entity Framework 批量插入很慢 Entity Framework 批量插入很慢吗? ...
- [(转)hystar整理]Entity Framework 教程
预备知识 2 LINQ技术 2 LINQ技术的基础 - C#3.0 2 自动属性 2 隐式类型 2 对象初始化器与集合初始化器 3 匿名类 3 扩展方法 ...
- [hystar整理]Entity Framework 教程
目录 预备知识 2 LINQ技术 2 LINQ技术的基础 - C#3.0 2 自动属性 2 隐式类型 2 对象初始化器与集合初始化器 3 匿名类 3 扩展方法 ...
- Asp.Net MVC 模型(使用Entity Framework创建模型类)
Asp.Net MVC 模型(使用Entity Framework创建模型类) 这篇教程的目的是解释在创建ASP.NET MVC应用程序时,如何使用Microsoft Entity Framework ...
- 《Entity Framework 6 Recipes》中文翻译系列 (45) ------ 第八章 POCO之获取原始对象与手工同步对象图和变化跟踪器...
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 8-6 获取原始对象 问题 你正在使用POCO,想从数据库获取原始对象. 解决方案 ...
- 关于Entity Framework自动关联查询与自动关联更新导航属性对应的实体注意事项说明...
一.首先了解下Entity Framework 自动关联查询: Entity Framework 自动关联查询,有三种方法:Lazy Loading(延迟加载),Eager Loading(预先加载) ...
- 一个用于 Entity Framework 对象拷贝的方法
此方法用于两个不同类型Entity Framework 对象相同名称属性之间的拷贝 public class EntityHelper { /// <summary> ///用" ...
- Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作
Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作 Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作 1>. 创建一个控制台程序 2 ...
- Entity Framework关联实体的三种加载方法
推荐文章 EF性能之关联加载 总结很好 一:介绍三种加载方式 Entity Framework作为一个优秀的ORM框架,它使得操作数据库就像操作内存中的数据一样,但是这种抽象是有性能代价的,故鱼和熊掌 ...
最新文章
- mysql auto_increment建表_在mysql表中创建一个ID auto_increment(在事实之后)
- LigerUI编辑表格组件单元格校验问题
- 子界类型的定义和应用
- [APIO/CTSC 2007]数据备份(贪心+堆)
- websocket 介绍及实现
- 这几个公众号带你看看BAT的工作情况
- [RL] pip 安装 atari-py
- [转]jquery的ajax交付时“加载中”提示的处理方法
- CentOS Repos
- python中关于命名的例子_Python()-类命名空间和对象/实例命名空间
- 李彦宏被泼水,是“多数人的暴力”还是“群众的宣泄”
- html5的优点与缺点大概总结
- ddk开发 c语言,ddk_helloWDM_原代码是网上高手的杰作
- 白话区块链 之 14 - ​区块链的技术意义
- 九章量子计算机 知乎,量子计算机《九章》问世 知乎微博消息: 北京时间 12 月 4 日凌晨 3 点,一篇重要文章以 First Releas... - 雪球...
- ansi编码_刨根究底字符编码之零——前言
- 前端开发(html和css)
- WebSpider简介
- Openstack Kilo安装错误汇总
- PTA 水题之7-20 镜子碎了