Programming Entity Framework-dbContext 学习笔记第五章
### Programming Entity Framework-dbContext 学习笔记 第五章
将图表添加到Context中的方式及容易出现的错误
方法 | 结果 | 警告 |
---|---|---|
Add Root | 图标中的所有实体将被跟踪,并标记为Added | SaveChage 将试图将所有实体插入数据库,即使数据库中已存在该实体 |
Attach Root | 所有实体将被跟踪并标记为Unchanged | 新添加的实体将不会被插入数据库,并容易造成主键冲突 |
Add or Attach Root,then paint state throughout graph | 所有的实体将拥有正确的状态值 | 建议使用 Add Root 而不是Attach Root,以避免新实体的主键冲突 |
DbEntityEntry类型包含属性来记录实体各个状态的值:
- CurrentValues 包含实体所有属性的当前值。
- OriginalValues 包含实体属性未被修改前的值。
- GetDatabaseValues() 方法返回实体属性目前在数据库中的值。
注:对于状态为Added 和 Deleted 的实体不包含后两个取值,试图读取时,将引发异常。
用于打印各个属性的代码:
private static void PrintPropertyValues(DbPropertyValues values) {foreach (var propertyName in values.PropertyNames){Console.WriteLine(" - {0}: {1}", propertyName,values[propertyName]); } }
Working with DbPropertyValues for Complex Types
- Code First 的默认约定是将不包含主键的实体理解为“复杂类型”,当需要将包含主键的实体定义为“复杂类型”时,可借助[ComplexType]特性进行标注。
当我们从DbPropertyValues 中获取的值为一个复杂对象时,它将被表示为一个新的 DbPropertyValues 对象。
下面是一个读取含有复杂属性的 DbPropertyValues 的代码
private static void PrintPropertyValues(DbPropertyValues values, int indent = 1)
{foreach (var propertyName in values.PropertyNames){var value = values[propertyName];if (value is DbPropertyValues){Console.WriteLine("{0}- Complex Property: {1}", string.Empty.PadLeft(indent),propertyName);PrintPropertyValues((DbPropertyValues)value, indent + 1);}else{Console.WriteLine("{0}- {1}: {2}", string.Empty.PadLeft(indent), propertyName, values[propertyName]);}}
}
Copying the Values from DbPropertyValues into an Entity
DbPropertyValues 包含一个 ToObject 方法,可以在不覆盖原有实例的情况下,将所有的值复制到一个新的实例对象。
注:ToObject 方法只复制标量属性,忽略导航属性。
Changing Values in a DbPropertyValues
DbPropertyValues 不是只读的,可以被修改。当你修改CurrentValues 的值的时候,将改变当前实例的值。
修改将自动触发 Changes Tracking.
Clone() 方法可以复制所有的DbPropertyValues ,新克隆出来的对象将不会被Change Tracker 跟踪。
Using the SetValues method
用户在修改了实体的属性值之后,想撤销所有的修改,最简单的方式就是利用 SetValues() 方法,将OriginalValues 中的数据拷贝到 CurrentValues 中。
代码如下:
entry.CurrentValues.SetValues(entry.OriginalValues); entry.State = EntityState.Unchanged;//因为SetVaues方法不会自动修改实体的状态,所以需要手动修改。
- SetValues方法不尽可以接受DbPropertyValues做为参数,也可以接受其他类型,该方法会自动查找同名属性的值进行覆盖,找不到任何值时,引发异常。
- 可以利用SetValues方法实现实体对象的克隆。直接上代码:
private static void CreateDavesCampsite()
{using (var context = new BreakAwayContext()){//从数据库中读取d.Name == "Dave's Dump"的实体var davesDump = (from d in context.Lodgings where d.Name == "Dave's Dump" select d).Single();//新建实体var clone = new Lodging();//添加到追踪,这样才可以进行复制context.Lodgings.Add(clone);//复制,这里传入的就不是DbPropertyValues 的实例context.Entry(clone).CurrentValues.SetValues(davesDump);//修改名称clone.Name = "Dave's Camp";//保存context.SaveChanges();Console.WriteLine("Name: {0}", clone.Name); //output:Dave's CampConsole.WriteLine("Miles: {0}", clone.MilesFromNearestAirport); //32.65Console.WriteLine("Contact Id: {0}", clone.PrimaryContactId); //1//只有名称被修改了。}
}
Working with Individual Properties
你可以使用 Property, Complex, Reference, and Collection 方法去获取或者操作单独的属性:
- Property 方法可以用来处理 Scalar 和 Complex 属性
- Complex 方法提供对复杂属性的附加特殊操作
- Reference 和 Collection 方法用于导航属性
- 还有一个Member方法,可以用于任何类型的属性。该方法不是强类型的,仅提供对属性通用信息的访问
Working with Scalar Properties
Property方法可以访问属性的原始值,当前值,和是否被修改等信息,该方法具有弱类型和强类型的两个重载。直接上代码:
private static void WorkingWithPropertyMethod()
{using (var context = new BreakAwayContext()){var davesDump = (from d in context.Lodgings where d.Name == "Dave's Dump" select d).Single();//此处会调用强类型的泛型方法,所以后面可以使用lambda表达式var entry = context.Entry(davesDump);//使用lambda表达式访问Name属性entry.Property(d => d.Name).CurrentValue = "Dave's Bargain Bungalows";Console.WriteLine("Current Value: {0}", entry.Property(d => d.Name).CurrentValue);Console.WriteLine("Original Value: {0}", entry.Property(d => d.Name).OriginalValue);Console.WriteLine("Modified?: {0}", entry.Property(d => d.Name).IsModified);}
}
输出结果:
Current Value: Dave's Bargain Bungalows
Original Value: Dave's Dump
Modified?: True
Working with Complex Properties
- 提供对复杂属性的操作。
Example 5-20. Accessing change tracking information for a complex property.
private static void WorkingWithComplexMethod()
{using (var context = new BreakAwayContext()){//从数据库检索实体var julie = (from p in context.People where p.FirstName == "Julie" select p).Single();//获取Entryvar entry = context.Entry(julie);//操作复杂属性,这里使用了Property方法操作复杂属性的属性。entry.ComplexProperty(p => p.Address).Property(a => a.State).CurrentValue = "VT";//以上方法可以用以下方法代替entry.Property(p => p.Address.State).CurrentValue = "VT";//又或者entry.Property("Address.State").CurrentValue = "VT";Console.WriteLine("Address.State Modified?: {0}", entry.ComplexProperty(p => p.Address).Property(a => a.State).IsModified); //trueConsole.WriteLine("Address Modified?: {0}", entry.ComplexProperty(p => p.Address).IsModified); //true//链式调用访问复杂属性中包含的复杂属性Console.WriteLine("Info.Height.Units Modified?: {0}", entry.ComplexProperty(p => p.Info).ComplexProperty(i => i.Height).Property(h => h.Units).IsModified);//false}
}
当操作复杂属性的时候,Entity Framework 会跟踪它的状态变化,但是不会追踪它的个别属性的变化,当修改其中任一个单独的属性时,所有属性的状态将变为Modified.
- 可以修改复杂属性的值
entry.ComplexProperty(p => p.Address).CurrentValue = new Address { State = "VT" };
该操作将会把整个复杂属性标记为:Modified.
Working with Navigation Properties
Reference and Collection 方法被用户访问导航属性
- Reference 方法用于访问单个实体
- Collection 方法用于访问集合属性
这些方法提供以下功能:
- 读写导航属性的当前值
- 从数据库加载关联数据
- 获取导航属性代表的查询(query)
Modifying the value of a navigation property
Example 5-21. Change tracking information for a reference navigation property
private static void WorkingWithReferenceMethod()
{using (var context = new BreakAwayContext()){//从数据库检索Name == "Dave's Dump"的实体对象var davesDump = (from d in context.Lodgings where d.Name == "Dave's Dump" select d).Single();//获取entryvar entry = context.Entry(davesDump);//获取并加载Destination导航属性,Load之前改属性为nullentry.Reference(l => l.Destination).Load();var canyon = davesDump.Destination;//输出导航属性destination 属性Name的当前值Console.WriteLine("Current Value After Load: {0}", entry.Reference(d => d.Destination).CurrentValue.Name);//从数据库检索 Name == "Great Barrier Reef" 的Destination 对象var reef = (from d in context.Destinations where d.Name == "Great Barrier Reef" select d).Single();//将导航属性修改为 reefentry.Reference(d => d.Destination).CurrentValue = reef;//输出修改后导航属性destination 属性Name的值Console.WriteLine("Current Value After Change: {0}", davesDump.Destination.Name);}
}
输出结果:
Current Value After Load: Grand Canyon Current Value
After Change: Great Barrier Reef
Modifying navigation properties with the change tracker
之前,我们在处理标量属性的时候,我们发现,当我们通过Change Tracker修改属性时,不必显示的调用DetectChanges()方法。改变就被跟踪到了。
对于导航属性,同样如此!
Working with collection navigation properties
Example 5-22. Method to explore interacting with a collection property
private static void WorkingWithCollectionMethod()
{using (var context = new BreakAwayContext()){//从数据库检索 Description == "Trip from the database" 的Trip对象var res = (from r in context.Reservations where r.Trip.Description == "Trip from the database" select r).Single();var entry = context.Entry(res);//获取并加载集合导航属性entry.Collection(r => r.Payments).Load();//输出导航属性包含记录数Console.WriteLine("Payments Before Add: {0}", entry.Collection(r => r.Payments).CurrentValue.Count);//添加一条新记录var payment = new Payment { Amount = 245 };//添加到Payments集合,确认被跟踪,这里容易出坑context.Payments.Add(payment);//将新对象添加到导航属性entry.Collection(r => r.Payments).CurrentValue.Add(payment);//输出导航属性包含记录数Console.WriteLine("Payments After Add: {0}", entry.Collection(r => r.Payments).CurrentValue.Count);}
}
输出结果:
Payments Before Add: 1
Payments After Add: 2
和之前的其他属性不同,修改集合导航属性后必须手动调用DetectChanges()方法来跟踪变化
Refreshing an Entity from the Database
Entity Framework 的 DbEntityEntry 对象包含Reload 方法来从数据库中加载最新数据。
Example 5-23. Reloading an entity from the database
private static void ReloadLodging()
{using (var context = new BreakAwayContext()){//从数据库检索数据var hotel = (from d in context.Lodgings where d.Name == "Grand Hotel" select d).Single();//使用原始的SQL语句修改数据库中的值context.Database.ExecuteSqlCommand(@"UPDATE dbo.Lodgings SET Name = 'Le Grand Hotel' WHERE Name = 'Grand Hotel'");Console.WriteLine("Name Before Reload: {0}", hotel.Name);Console.WriteLine("State Before Reload: {0}", context.Entry(hotel).State);//重新加载数据context.Entry(hotel).Reload();Console.WriteLine("Name After Reload: {0}", hotel.Name);Console.WriteLine("State After Reload: {0}", context.Entry(hotel).State);}
}
数据结果
Name Before Reload: Grand Hotel
State Before Reload: Unchanged
Name After Reload: Le Grand Hotel
State After Reload: Unchanged
如果在从新加载数据之前对实体进行了修改 比如
hotel.Name = "A New Name";
输出结果将变为:
Name Before Reload: A New Name
State Before Reload: Modified
Name After Reload: Le Grande Hotel
State After Reload: Unchanged
Change Tracking Information and Operations for Multiple Entities
前面操纵的都是单个的实体,接下来我们介绍DbContext.ChangeTracker.Entries 方法,该方法包含两个
重载,一个是泛型的,一个是非泛型的。泛型的方法返回指定类型的记录结合。非泛型的重载
返回一个DbEntityEntry类型的集合,包含所有被追踪的实体。
Example 5-24. Iterating over all entries from the change tracker
private static void PrintChangeTrackerEntries()
{using (var context = new BreakAwayContext()){//从数据库检索Description == "Trip from the database"的Reservations类型的对象var res = (from r in context.Reservations where r.Trip.Description == "Trip from the database" select r).Single();//加载它的集合属性 Paymentscontext.Entry(res).Collection(r => r.Payments).Load();//添加一个新的Paymentres.Payments.Add(new Payment { Amount = 245 });//使用非泛型的方法返回所有被追踪的对象var entries = context.ChangeTracker.Entries();//迭代输出类型和状态foreach (var entry in entries){Console.WriteLine("Entity Type: {0}", entry.Entity.GetType());Console.WriteLine(" - State: {0}", entry.State);}}
}
输出结果
Entity Type: Model.Payment
- State: Added
Entity Type: Model.Reservation
- State: Unchanged
Entity Type: Model.Payment
- State: Unchanged
Using the Change Tracker API in Application Scenarios
Resolving Concurrency Conflicts
默认情况下 Entity Framework 总是更新所有的改变,而不管是不是存在并发冲突,但是你可以
配置你的Model,当并发冲突发生时,抛出一个异常。你可以将一个特定的属性指定为 concurrency token
转载于:https://www.cnblogs.com/jameszh/p/6715460.html
Programming Entity Framework-dbContext 学习笔记第五章相关推荐
- 《Go语言圣经》学习笔记 第五章函数
<Go语言圣经>学习笔记 第五章 函数 目录 函数声明 递归 多返回值 匿名函数 可变参数 Deferred函数 Panic异常 Recover捕获异常 注:学习<Go语言圣经> ...
- 2022 最新 Android 基础教程,从开发入门到项目实战【b站动脑学院】学习笔记——第五章:中级控件
第 5 章 中级控件 本章介绍App开发常见的几类中级控件的用法,主要包括:如何定制几种简单的图形.如何使用几种选择按钮.如何高效地输入文本.如何利用对话框获取交互信息等,然后结合本章所学的知识,演示 ...
- 《Android深度探究HAL与驱动开发》学习笔记----第五章
第五章 搭建S3C6410开发板的测试环境 开发板是开发和学习嵌入式技术的主要硬件设备. 主要学习了搭建S3C6410开发板的测试环境.首先要了解到S3C6410是一款低功耗.高性价比的RISC处理器 ...
- muduo学习笔记 - 第五章 高效的多线程日志
第五章 高效的多线程日志 日志有两种意思: 诊断日志 交易日志 本章讲的是前一种日志,文本的供人阅读的日志,通常用于故障诊断和追踪,也可用于性能分析. 日志通常要记录: 收到的每条消息的id(关键字段 ...
- javascript高级程序设计 学习笔记 第五章 上
第五章 引用类型的值(对象)是引用类型的一个实例.在 ECMAScript 中,引用类型是一种数据结构, 用于将数据和功能组织在一起.它也常被称为类,但这种称呼并不妥当.尽管 ECMAScri ...
- css层叠样式表基础学习笔记--第五章 文本属性
第五章 文本属性 5-01 字间距 5-02 行高 5-03 首行缩进 5-04 水平排列方式 5-05 垂直对齐方式 5-06 文本修饰 5-07 文本阴影 5-08 文本属性重置 5-01 字间距 ...
- 程序设计与算法三~C++面向对象程序设计~北大郭炜MOOC学习笔记~第五章:继承与派生(新标准C++程序设计)
以下内容为笔者手打,望读者珍惜,如有转载还请注明. 第五章 继承与派生 $5.1 继承与派生的概念 $5.1.1 基本概念 在C++中,当定义一个新的类B时,如果发现类B拥有某个已经写好的类A ...
- 《谁说菜鸟不会数据分析》学习笔记 第五章数据分析
第五章 数据分析 5.1 数据分析方法 现状分析 5.1.1 对比分析法 5.1.2 分组分析法 5.1.3 结构分析法 5.1.4 分布分析法 5.1.5 交叉分析法 5.1.6 RFM分析法 5. ...
- AE学习笔记——第五章:效果预设和渲染导出
目录 一:效果与预设的应用 (1)效果的运算顺序 (2)效果的应用 (3)动画预设的使用 二:渲染和输出 (1)使用Adobe Media Encoder渲染 (2)使用AE渲染 A:.avi格式 B ...
最新文章
- STM32F103 与 STM32F407引脚兼容问题
- 一次使用 Eclipse Memory Analyzer 分析 Tomcat 内存溢出
- gitlab + Jenkins
- ASP.NET ASHX中获得Session
- 6万人砍不下来一部拼多多手机,背后原来是这个原因。
- qq linux版本下载官网下载,腾讯QQ For Linux
- (36)System Verilog类中方法示例
- UTF-8字符集成为Java 18默认字符集?发布周期将至,Java 18现身
- mysql alisql,Mysql-03. ubuntu 安装 alisql
- 浅析export * from 与 export {default} from用法
- 基于51单片机的单词记忆测试器
- input密码框显示与隐藏
- php用do while实现斐波那契数列,php实现斐波那契数列
- VR家装:智慧家装“黑科技”
- Deepin 20社区版设置双屏显示
- 历年至今TVB剧集目录(持续更新...我已看过的推荐)
- 用 texstudio, 外部 pdf 浏览器查看可以正常显示中文, 但是内置的 pdf 浏览器不能显示中文?
- 获得网易云音乐歌曲播放的url
- [微信支付 ] prepay_id 为空,可能出现的问题?微信支付失败
- JavaWeb开发分享:WRO