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 定义字体 ...
随机推荐
- java 邮件发送工具类【来源网络自己已经实际应用】
最近在做一个Java发送邮件的工具类,现在分享一下完整的代码 首先需要java邮件的包javax.mail-1.5.4.jar 之前因为链接给错了,很不好意思,现在重新发一次. 包在这里可以下载htt ...
- C#/.NET 中推荐的 Dispose 模式的实现
如果你觉得你的类需要实现 IDisposable 接口,还是需要注意一些坑的.不过前人准备了 Dispose 模式 供我们参考,最大程度避免这样的坑. C#程序中的 Dispose 方法,一旦被调用了 ...
- 《DSP using MATLAB》示例 Example 6.14、6.15
- [BZOJ5329][SDOI2018]战略游戏
bzoj luogu Description 省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏. 这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任 ...
- MySQL安装与操作总结
安装MySQL 添加mysql源 # rpm -Uvh http://repo.mysql.com//mysql57-community-release-el7-7.noarch.rpm 安装mysq ...
- LeetCode 755. Pour Water
原题链接在这里:https://leetcode.com/problems/pour-water/description/ 题目: We are given an elevation map, hei ...
- ft,dtft,dft的关系(转载)
很多同学学习了数字信号处理之后,被里面的几个名词搞的晕头转向,比如DFT,DTFT,DFS,FFT,FT,FS等,FT和FS属于信号与系统课程的内容,是对连续时间信号的处理,这里就不过多讨论,只解释一 ...
- Adreno Profiler 提取手机游戏资源
https://blog.csdn.net/lly20000/article/details/79774755 step.1 准备工具 1.adb连接工具(我用的cofface adb ) [cof ...
- bzoj 2395 [Balkan 2011]Timeismoney——最小乘积生成树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2395 如果把 \( \sum t \) 作为 x 坐标,\( \sum c \) 作为 y ...
- win10下安装并启动zookeeper
下载直接到zk的官网(zookeeper.apache.org)即可,点击右边的Releases,在Download下再点Download进入镜像下载页面,在给出的链接列表里选择一个镜像地址,进去后选 ...