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 Camp
Console.WriteLine("Miles: {0}", clone.MilesFromNearestAirport); //32.65
Console.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();
//获取Entry
var 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); //true
Console.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();
//获取entry
var entry = context.Entry(davesDump);
//获取并加载Destination导航属性,Load之前改属性为null
entry.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();
//将导航属性修改为 reef
entry.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();
//加载它的集合属性 Payments
context.Entry(res).Collection(r => r.Payments).Load();
//添加一个新的Payment
res.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
Programming Entity Framework-dbContext 学习笔记第五章的更多相关文章
- Entity Framework 6 学习笔记2 — 增、删、改、显示简单代码示例
前言 通过 “Entity Framework 6 学习笔记1 — 介绍和安装方法”文章我相信大家对EF的安装应该没什么问题了,整体安装还是比较简单的,只需要通过Nuge搜索EF然后安装就可以了,这也 ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第一章:向量代数
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第一章:向量代数 学习目标: 学习如何使用几何学和数字描述 Vecto ...
- [HeadFrist-HTMLCSS学习笔记]第五章认识媒体:给网页添加图像
[HeadFrist-HTMLCSS学习笔记]第五章认识媒体:给网页添加图像 干货 JPEG.PNG.GIF有何不同 JPEG适合连续色调图像,如照片:不支持透明度:不支持动画:有损格式 PNG适合单 ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第九章:贴图
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第九章:贴图 代码工程地址: https://github.com/j ...
- Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二章:矩阵代数
原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二章:矩阵代数 学习目标: 理解矩阵和与它相关的运算: 理解矩阵的乘 ...
- 【马克-to-win】学习笔记—— 第五章 异常Exception
第五章 异常Exception [学习笔记] [参考:JDK中文(类 Exception)] java.lang.Object java.lang.Throwable java.lang.Except ...
- 《Spring实战》学习笔记-第五章:构建Spring web应用
之前一直在看<Spring实战>第三版,看到第五章时发现很多东西已经过时被废弃了,于是现在开始读<Spring实战>第四版了,章节安排与之前不同了,里面应用的应该是最新的技术. ...
- opencv图像处理基础 (《OpenCV编程入门--毛星云》学习笔记一---五章)
#include <QCoreApplication> #include <opencv2/core/core.hpp> #include <opencv2/highgu ...
- 学习笔记 第五章 使用CSS美化网页文本
第五章 使用CSS美化网页文本 学习重点 定义字体类型.大小.颜色等字体样式: 设计文本样式,如对齐.行高.间距等: 能够灵活设计美观.实用的网页正文版式. 5.1 字体样式 5.1.1 定义字体 ...
随机推荐
- GitHub项目为己所用
1.下载 2.cmd 进入文件夹 3.mvn clean package 4.mvn install
- Navicat工具导出mySQL数据库某个视图结构的.sql脚本
用Navicat工具怎么都导不出来mySQL数据库的某个视图.sql脚本,即使导出来也只是包含视图记录,不包含视图结构.经过一番研究,终于克服,操作如下: 1.在某个数据库中,新建备份,如下图 2.选 ...
- python动态给对象或者类添加方法
参考:http://stackoverflow.com/questions/972/adding-a-method-to-an-existing-object In Python, there is ...
- 转发 Java火焰图在Netflix的实践
为了分析不同软件或软件的不同版本使用CPU的情况,相关设计人员通常需要进行函数的堆栈性能分析.相比于定期采样获得数据的方式,利用定时中断来收集程序运行时的PC寄存器值.函数地址以及整个堆栈轨迹更加高效 ...
- npm dose not support Node.js v10.15.3
事件起因: 楼主在vue-cli官网,尝试使用vue-cli3脚手架+yarn包管理器构建项目时,命令行窗口提示node版本不对.如下图 这个大家都知道该如何去解决,直接去node官网下载符合版本的n ...
- Face detection in color images, 彩色图像中的人脸检测
人脸检测在视频监督,人机交互,人脸识别和人脸图像数据库管理等应用领域处于很重要的地位. 论文<Face detection in color images>中给出一种在YCbCr空间检测人 ...
- Java运算符,位运算
注意:位运算符针对整数的补码进行运算,所以运算结果也是补码 &(与运算) 将数据转化为补码形式,然后将0看作false,将1看作true,按位进行与运算,最后将结果转化为十进制来显示 ...
- Thread之五:线程的优先级
Java线程可以有优先级的设定,高优先级的线程比低优先级的线程有更高的几率得到执行(不完全正确,请参考下面的“线程优先级的问题“). 记住当线程的优先级没有指定时,所有线程都携带普通优先级. 优先级可 ...
- Redis:目录
ylbtech-Redis:目录 1.返回顶部 2.返回顶部 3.返回顶部 4.返回顶部 5.返回顶部 6.返回顶部 7.返回顶部 8.返回顶部 9.返回顶部 ...
- Vim编辑器基本操作学习(一)
最近在服务端编辑文件总不可避免要使用vim编辑器,下面就对学习到的常用命令进行总结,以便自己以后查看. 基本编辑命令 删除字符:x 删除一行:dd 删除换行符:J,同时将两行合并成一行 撤 ...