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重复插入关联对象相关推荐

  1. Entity Framework 批量插入

    为什么80%的码农都做不了架构师?>>>    奋斗的小鸟--dogxuefeng Entity Framework 批量插入很慢 Entity Framework 批量插入很慢吗? ...

  2. [(转)hystar整理]Entity Framework 教程

    预备知识    2 LINQ技术    2 LINQ技术的基础 - C#3.0    2 自动属性    2 隐式类型    2 对象初始化器与集合初始化器    3 匿名类    3 扩展方法    ...

  3. [hystar整理]Entity Framework 教程

    目录 预备知识    2 LINQ技术    2 LINQ技术的基础 - C#3.0    2 自动属性    2 隐式类型    2 对象初始化器与集合初始化器    3 匿名类    3 扩展方法 ...

  4. Asp.Net MVC 模型(使用Entity Framework创建模型类)

    Asp.Net MVC 模型(使用Entity Framework创建模型类) 这篇教程的目的是解释在创建ASP.NET MVC应用程序时,如何使用Microsoft Entity Framework ...

  5. 《Entity Framework 6 Recipes》中文翻译系列 (45) ------ 第八章 POCO之获取原始对象与手工同步对象图和变化跟踪器...

    翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 8-6  获取原始对象 问题 你正在使用POCO,想从数据库获取原始对象. 解决方案 ...

  6. 关于Entity Framework自动关联查询与自动关联更新导航属性对应的实体注意事项说明...

    一.首先了解下Entity Framework 自动关联查询: Entity Framework 自动关联查询,有三种方法:Lazy Loading(延迟加载),Eager Loading(预先加载) ...

  7. 一个用于 Entity Framework 对象拷贝的方法

    此方法用于两个不同类型Entity Framework 对象相同名称属性之间的拷贝 public class EntityHelper { /// <summary> ///用" ...

  8. Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作

    Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作 Entity Framework 的小实例:在项目中添加一个实体类,并做插入操作 1>. 创建一个控制台程序 2 ...

  9. Entity Framework关联实体的三种加载方法

    推荐文章 EF性能之关联加载 总结很好 一:介绍三种加载方式 Entity Framework作为一个优秀的ORM框架,它使得操作数据库就像操作内存中的数据一样,但是这种抽象是有性能代价的,故鱼和熊掌 ...

最新文章

  1. mysql auto_increment建表_在mysql表中创建一个ID auto_increment(在事实之后)
  2. LigerUI编辑表格组件单元格校验问题
  3. 子界类型的定义和应用
  4. [APIO/CTSC 2007]数据备份(贪心+堆)
  5. websocket 介绍及实现
  6. 这几个公众号带你看看BAT的工作情况
  7. [RL] pip 安装 atari-py
  8. [转]jquery的ajax交付时“加载中”提示的处理方法
  9. CentOS Repos
  10. python中关于命名的例子_Python()-类命名空间和对象/实例命名空间
  11. 李彦宏被泼水,是“多数人的暴力”还是“群众的宣泄”
  12. html5的优点与缺点大概总结
  13. ddk开发 c语言,ddk_helloWDM_原代码是网上高手的杰作
  14. 白话区块链 之 14 - ​区块链的技术意义
  15. 九章量子计算机 知乎,量子计算机《九章》问世 知乎微博消息: 北京时间 12 月 4 日凌晨 3 点,一篇重要文章以 First Releas... - 雪球...
  16. ansi编码_刨根究底字符编码之零——前言
  17. 前端开发(html和css)
  18. WebSpider简介
  19. Openstack Kilo安装错误汇总
  20. PTA 水题之7-20 镜子碎了

热门文章

  1. tif文件转pdf_PPT怎么转换成PDF文件?可以帮到你的PPT转PDF方法
  2. 友盟小米收不到推送消息_App消息推送的到达率多少才是正常的?
  3. Qt程序打包发布方法(使用官方提供的windeployqt工具)
  4. 获取当天0点数据scala实现
  5. LRU缓存机制—leetcode146
  6. InsightFace及其mxnet、tensorflow代码实现
  7. arm汇编解析—qnnpack卷积实现
  8. Apache配置同一IP使用多域名对应多个网站
  9. FatFsVersion0.01源码分析
  10. 嵌入式系统HAL原理与BSP的实现方法