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. 现在很火的数据科学到底是什么?你对做DATA SCIENTIST感兴趣吗?

    转自– Warald (Email: iamxiaoning@gmail.com) 博客: http://www.1point3acres.com,微博:http://www.weibo.com/wa ...

  2. MPI 学习

    一.编译MPI mpic++ test.cc -o test 二.启动MPI mpiexec -np 10 ./test 三.几个例子 第一个进程向第二个发一个数,第二个进程向第三个进程发送一个数.. ...

  3. Jmeter聚合报告

    Label:请求的Name. #Samples:发出请求数量. Average:平均响应时间(单位:ms). Median:全部响应时间中位数,. 90%Line:90%用户的响应时间低于这个时间. ...

  4. eclipse如何通过git把项目上传到码云上

    转载:原文链接:https://www.cnblogs.com/yixtx/p/8310311.html 1.eclipse安装git插件 具体我也做过,因为我下载的eclipse版本以及由git插件 ...

  5. BZOJ4557 JLoi2016 侦察守卫 【树形DP】*

    BZOJ4557 JLoi2016 侦察守卫 Description 小R和B神正在玩一款游戏.这款游戏的地图由N个点和N-1条无向边组成,每条无向边连接两个点,且地图是连通的.换句话说,游戏的地图是 ...

  6. 20179223《Linux内核原理与分析》第八周学习笔记

    视频学习 可执行文件是怎么得来的? .c汇编成汇编代码.asm,然后再汇编成目标码.o.然后在连接成可执行文件,然后加载到内存可执行了. 对hello.c文件预处理(cpp),预处理负责把includ ...

  7. python 读取 xlsx

    >>> xl = pd.ExcelFile("dummydata.xlsx") >>> xl.sheet_names [u'Sheet1', u ...

  8. #231-D: declaration is not visible outside of function

    通常出现在.h中的函数声明,函数声明中的变量用到了.h当中没有的变量形式,比如特殊定义的结构体

  9. (转)完美解决 Android WebView 文本框获取焦点后自动放大有关问题

    完美解决 Android WebView 文本框获取焦点后自动放大问题 前几天在写一个项目时,要求在项目中嵌入一个WebView 本来很快就完成了,测试也没有问题.但发给新加坡时,他们测试都会出现文本 ...

  10. jdk1.8新特性之接口default方法

    众所周知,default是java的关键字之一,使用场景是配合switch关键字用于条件分支的默认项.但自从java的jdk1.8横空出世以后,它就被赋予了另一项很酷的能力——在接口中定义非抽象方法. ...