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类型包含属性来记录实体各个状态的值:

  1. CurrentValues 包含实体所有属性的当前值。
  2. OriginalValues 包含实体属性未被修改前的值。
  3. 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

  1. Code First 的默认约定是将不包含主键的实体理解为“复杂类型”,当需要将包含主键的实体定义为“复杂类型”时,可借助[ComplexType]特性进行标注。
  2. 当我们从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

  1. DbPropertyValues 包含一个 ToObject 方法,可以在不覆盖原有实例的情况下,将所有的值复制到一个新的实例对象。

注:ToObject 方法只复制标量属性,忽略导航属性。

Changing Values in a DbPropertyValues

  1. DbPropertyValues 不是只读的,可以被修改。当你修改CurrentValues 的值的时候,将改变当前实例的值。

修改将自动触发 Changes Tracking.

  1. Clone() 方法可以复制所有的DbPropertyValues ,新克隆出来的对象将不会被Change Tracker 跟踪。

Using the SetValues method

  1. 用户在修改了实体的属性值之后,想撤销所有的修改,最简单的方式就是利用 SetValues() 方法,将OriginalValues 中的数据拷贝到 CurrentValues 中。

代码如下:

 entry.CurrentValues.SetValues(entry.OriginalValues);
entry.State = EntityState.Unchanged;//因为SetVaues方法不会自动修改实体的状态,所以需要手动修改。
  1. SetValues方法不尽可以接受DbPropertyValues做为参数,也可以接受其他类型,该方法会自动查找同名属性的值进行覆盖,找不到任何值时,引发异常。
  2. 可以利用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
  1. 提供对复杂属性的操作。

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.

  1. 可以修改复杂属性的值
entry.ComplexProperty(p => p.Address).CurrentValue = new Address { State = "VT" };

该操作将会把整个复杂属性标记为:Modified.

Working with Navigation Properties

Reference and Collection 方法被用户访问导航属性

  • Reference 方法用于访问单个实体
  • Collection 方法用于访问集合属性

这些方法提供以下功能:

  1. 读写导航属性的当前值
  2. 从数据库加载关联数据
  3. 获取导航属性代表的查询(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 学习笔记第五章的更多相关文章

  1. Entity Framework 6 学习笔记2 — 增、删、改、显示简单代码示例

    前言 通过 “Entity Framework 6 学习笔记1 — 介绍和安装方法”文章我相信大家对EF的安装应该没什么问题了,整体安装还是比较简单的,只需要通过Nuge搜索EF然后安装就可以了,这也 ...

  2. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第一章:向量代数

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第一章:向量代数 学习目标: 学习如何使用几何学和数字描述 Vecto ...

  3. [HeadFrist-HTMLCSS学习笔记]第五章认识媒体:给网页添加图像

    [HeadFrist-HTMLCSS学习笔记]第五章认识媒体:给网页添加图像 干货 JPEG.PNG.GIF有何不同 JPEG适合连续色调图像,如照片:不支持透明度:不支持动画:有损格式 PNG适合单 ...

  4. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第九章:贴图

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第九章:贴图 代码工程地址: https://github.com/j ...

  5. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二章:矩阵代数

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二章:矩阵代数 学习目标: 理解矩阵和与它相关的运算: 理解矩阵的乘 ...

  6. 【马克-to-win】学习笔记—— 第五章 异常Exception

    第五章 异常Exception [学习笔记] [参考:JDK中文(类 Exception)] java.lang.Object java.lang.Throwable java.lang.Except ...

  7. 《Spring实战》学习笔记-第五章:构建Spring web应用

    之前一直在看<Spring实战>第三版,看到第五章时发现很多东西已经过时被废弃了,于是现在开始读<Spring实战>第四版了,章节安排与之前不同了,里面应用的应该是最新的技术. ...

  8. opencv图像处理基础 (《OpenCV编程入门--毛星云》学习笔记一---五章)

    #include <QCoreApplication> #include <opencv2/core/core.hpp> #include <opencv2/highgu ...

  9. 学习笔记 第五章 使用CSS美化网页文本

    第五章   使用CSS美化网页文本 学习重点 定义字体类型.大小.颜色等字体样式: 设计文本样式,如对齐.行高.间距等: 能够灵活设计美观.实用的网页正文版式. 5.1 字体样式 5.1.1 定义字体 ...

随机推荐

  1. java 邮件发送工具类【来源网络自己已经实际应用】

    最近在做一个Java发送邮件的工具类,现在分享一下完整的代码 首先需要java邮件的包javax.mail-1.5.4.jar 之前因为链接给错了,很不好意思,现在重新发一次. 包在这里可以下载htt ...

  2. C#/.NET 中推荐的 Dispose 模式的实现

    如果你觉得你的类需要实现 IDisposable 接口,还是需要注意一些坑的.不过前人准备了 Dispose 模式 供我们参考,最大程度避免这样的坑. C#程序中的 Dispose 方法,一旦被调用了 ...

  3. 《DSP using MATLAB》示例 Example 6.14、6.15

  4. [BZOJ5329][SDOI2018]战略游戏

    bzoj luogu Description 省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏. 这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任 ...

  5. MySQL安装与操作总结

    安装MySQL 添加mysql源 # rpm -Uvh http://repo.mysql.com//mysql57-community-release-el7-7.noarch.rpm 安装mysq ...

  6. LeetCode 755. Pour Water

    原题链接在这里:https://leetcode.com/problems/pour-water/description/ 题目: We are given an elevation map, hei ...

  7. ft,dtft,dft的关系(转载)

    很多同学学习了数字信号处理之后,被里面的几个名词搞的晕头转向,比如DFT,DTFT,DFS,FFT,FT,FS等,FT和FS属于信号与系统课程的内容,是对连续时间信号的处理,这里就不过多讨论,只解释一 ...

  8. Adreno Profiler 提取手机游戏资源

    https://blog.csdn.net/lly20000/article/details/79774755 step.1 准备工具 1.adb连接工具(我用的cofface adb  ) [cof ...

  9. bzoj 2395 [Balkan 2011]Timeismoney——最小乘积生成树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2395 如果把 \( \sum t \) 作为 x 坐标,\( \sum c \) 作为 y ...

  10. win10下安装并启动zookeeper

    下载直接到zk的官网(zookeeper.apache.org)即可,点击右边的Releases,在Download下再点Download进入镜像下载页面,在给出的链接列表里选择一个镜像地址,进去后选 ...