数据加载

如下这样的一个lamda查询语句,不会立马去查询数据库,只有当需要用时去调用(如取某行,取某个字段、聚合),才会去操作数据库,EF中本身的查询方法返回的都是IQueryable接口。

链接:IEnumerable和IQueryable接口说明

其中聚合函数会影响数据加载,诸如:toList(),sum(),Count(),First()能使数据立即查询加载。

IQueryable中的Load方法

一般情况,我们都是使用ToList或First来完成预先加载数据操作。但在EF中还可以使用Load() 方法来显式加载,将获取的数据放到EF Context中,缓存起来备用。和ToList()很像,只是它不创建列表只是把数据缓存到EF Context中而已,开销较少。

using (var context = new TestDB())
{
context.Place.Where(t=>t.PlaceID==).Load();
}

VS中的方法说明:

延迟加载

用之前的Place类和People为例

Place对象如下:

public class Place
{
[Key]
public int PlaceID { get; set;} public string Provice { get; set; } public string City { get; set; }
//导航属性
public virtual List<People> Population { get; set; } }

下面查询,不会主动去查询出导航属性(Population )关联的数据

using (var context = new TestDB())
{
var obj = context.Place.Where(t => t.PlaceID == ).FirstOrDefault();
}

可以看到Population为null

只有用到Population对象时,EF才会发起到数据库的查询;

当然导航数据必须标记virtual,配置延迟加载

//导航属性
public virtual Place Place { get; set; }

要注意的事:在延迟加载条件下,经常以为导航数据也加载了,从而在循环中去遍历导航属性,造成多次访问数据库。

立即加载

除了前面所说的,使用聚合函数(sum等)外来立即预加载数据,还可以使用Include方法

在上面的查询中,想要查询place以及关联的Population数据如下:

using (var context = new TestDB())
{
var obj = context.Place.Where(t => t.PlaceID == ).Include(p=>p.Population).FirstOrDefault();
}

事务

在EF中,saveChanges()默认是开启了事务的,在调用saveChanges()之前,所有的操作都在同一个事务中,同一次数据库连接。若使用同一DbContext对象,EF的默认事务处理机制基本满足使用。

除此之外,以下两种情况怎么使用事务:

  1. 数据分阶段保存,多次调用saveChanges()
  2. 使用多个DbContext对象(尽量避免)

第一种情况:显式事务

using (var context = new TestDB())
{
using (var tran=context.Database.BeginTransaction())
{
try
{
context.Place.Add(new Place { City = "beijing", PlaceID = });
context.SaveChanges();
context.People.Add(new People { Name = "xiaoli" });
context.SaveChanges();
tran.Commit();
}
catch (Exception)
{
tran.Rollback();
}
}
}

注意的是,不调用commit()提交,没有异常事务也不会默认提交。

第二种情况:TransactionScope分布式事务

  • 引入System.Transactions.dll
  • Windows需要开启MSDTC
  • TransactionScope也于适用于第一种情况。这里只讨论连接多个DBcontext的事务使用
  • 需要调用Complete(),否则事务不会提交
  • 在事务内,报错会自动回滚
using (var tran = new TransactionScope())
{
try
{
using (var context = new TestDB())
{
context.Place.Add(new Place { City = ""});
context.SaveChanges();
}
using (var context2 = new TestDB2())
{
context2.Student.Add(new Student { Name="li"});
context2.SaveChanges();
}
throw new Exception();
tran.Complete();
}
catch (Exception)
{ }
}

注意:上面代码在同一个事务内使用了多个DBcontext,会造次多次连接关闭数据库

题外话

如是多个DBcontext连着是同一个数据库的话,可以将一个己打开的数据库连接对象传给它,并且需要指定EF在DbContext对象销毁时不关闭数据库连接。避免造成多次连接关闭数据库

DbContext对象改造,增加重载构造函数;;传入两个参数

  • 数据库连接DbConnection
  • contextOwnsConnection=false(DbContext对象销毁时不关闭数据库连接):
public class TestDB2 : DbContext
{
public TestDB2():base("name=Test")
{
}
public TestDB2(DbConnection conn, bool contextOwnsConnection) : base(conn, contextOwnsConnection)
{
}
public DbSet<Student> Student { get; set; }
}

事务代码如下:

using (TransactionScope scope = new TransactionScope())
{
String connStr = ……;
using (var conn = SqlConnection(connStr))
{
try
{
conn.Open();
using (var context1 = new MyDbContext(conn, contextOwnsConnection: false))
{
             ……
                context1.SaveChanges();
}
using (var context2 = new MyDbContext(conn, contextOwnsConnection: false))
{
……
context2.SaveChanges();
}
       scope.Complete();
}
catch (Exception e)
{ }
finally
{
conn.Close();
}
}
}

DBcontent线程内唯一

链接:dbcontext实例创建问题

并发

在实际场景中,并发是很常见的事,同条记录同时被不同的两个用户修改

在EF中有两种常见的并发冲突检测

方法一:ConcurrencyCheck特性

可以指定对象的一个或多个属性用于并发检测,在对应属性加上ConcurrencyCheck特性

这里我们指定Student 对象的属性Name

public class Student
{
[Key]
public int ID { get; set; }
[ConcurrencyCheck]
public string Name { get; set; }
public int Age { get; set; }
}

用个两个线程同时去更新Student对象,模拟用户并发操作

static void Main(string[] args)
{
Task t1 = Task.Run(() => {
using (var context = new TestDB2())
{
var obj = context.Student.First();
obj.Name = "LiMing";
context.SaveChanges();
}
});
Task t2 = Task.Run(() => {
using (var context = new TestDB2())
{
var obj = context.Student.First();
obj.Age = ;
context.SaveChanges();
}
});
Task.WaitAll(t1,t2); }

并发冲突报错:

查看了sql server profiler,发现加了[ConcurrencyCheck]的属性名和值将出现在Where子句中

exec sp_executesql N'UPDATE [dbo].[Students]
SET [Age] = @
WHERE (([ID] = @) AND ([Name] = @))
',N'@ int,@ int,@ nvarchar(max) ',@0=26,@1=1,@2=N'WANG'

很显然:

t2再修改Age,根据并发检测属性Name的值已被改变,有其他用户在修改同一条数据,并发冲突。

为每个实体类都单独地设定检测属性实在太麻烦,应该由数据库来设定特殊字段值并维护更新会更好,下面就是另一种方法

方法二:timestamp

创建一个基类Base,指定一个特殊属性值,SQL Server中相应的字段类型为timestamp,自己项目中的实体类都可以继承它,

public class Base
{
[Timestamp]
public byte[] RowVersion { get; set; }
}

Student先基础base类,每次更新Student数据,RowVersion 字段就会由数据库生成一个新的值,根据这个特殊字段来检测并发冲突;实体类不再去考虑设置那个属性值和更新。

并发处理

同时更新并发,EF会抛出:DbUpdateConcurrencyException

两个更新线程如上:t1和t2

处理一

Task t1 = Task.Run(() => {
using (var context = new TestDB())
{
try
{
var obj = context.Student.First();
obj.Name = "LiMing2";
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
//从数据库重新加载数据并覆盖当前保存失败的对象
ex.Entries.Single().Reload();
context.SaveChanges();
}
}
});

也就是说,t1并发冲突更新失败,会重新从数据库拉取对象覆盖当前失败的对象,t1原本的更新被作废,于此同时的其他用户并发操作,如t2的更新将会被保存下来

处理二

Task t1 = Task.Run(() => {
using (var context = new TestDB())
{
try
{
var obj = context.Student.First();
obj.Name = "LiMing2";
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
context.SaveChanges();
}
}
});

从数据库重新获取值来替换保存失败的对象的属性原始值,再次提交更改,数据库就不会因为当前更新操作获取的原始值与数据库里现有值不同而产生异常(如检测属性的值已成一样),t1的更新操作就能顺利提交,其他并发操作如t2被覆盖

Entity Framework 查漏补缺 (二)的更多相关文章

  1. Entity Framework 查漏补缺 (一)

    明确EF建立的数据库和对象之间的关系 EF也是一种ORM技术框架, 将对象模型和关系型数据库的数据结构对应起来,开发人员不在利用sql去操作数据相关结构和数据.以下是EF建立的数据库和对象之间关系 关 ...

  2. Entity Framework 查漏补缺 (三)

    Code First的数据库映射 有两种方式来实现数据库映射: 数据属性:Data Annotation 映射配置: Fluent API 有继承关系的实体如何映射? Code First在生成数据库 ...

  3. 【Android面试查漏补缺】之事件分发机制详解

    前言 查漏补缺,查漏补缺,你不知道哪里漏了,怎么补缺呢?本文属于[Android面试查漏补缺]系列文章第一篇,持续更新中,感兴趣的朋友可以[关注+收藏]哦~ 本系列文章是对自己的前段时间面试经历的总结 ...

  4. 20165223 week1测试查漏补缺

    week1查漏补缺 经过第一周的学习后,在蓝墨云班课上做了一套31道题的小测试,下面是对测试题中遇到的错误的分析和总结: 一.背记题 不属于Java后继技术的是? Ptyhon Java后继技术有? ...

  5. Django 查漏补缺

    Django 查漏补缺 Django  内容回顾: 一. Http 请求本质: 网络传输,运用socket Django程序: socket 服务端 a. 服务端监听IP和端口 b. 浏览器发送请求 ...

  6. Java基础查漏补缺(2)

    Java基础查漏补缺(2) apache和spring都提供了BeanUtils的深度拷贝工具包 +=具有隐形的强制转换 object类的equals()方法容易抛出空指针异常 String a=nu ...

  7. Java基础查漏补缺(1)

    Java基础查漏补缺 String str2 = "hello"; String str3 = "hello"; System.out.println(str3 ...

  8. CSS基础面试题,快来查漏补缺

    本文大部分问题来源:50道CSS基础面试题(附答案),外加一些面经. 我对问题进行了分类整理,并给了自己的回答.大部分知识点都有专题链接(来源于本博客相关文章),用于自己前端CSS部分的查漏补缺.虽作 ...

  9. Asp.Net Core 查漏补缺《一》 —— IStartFilter

    Asp.Net Core 查漏补缺<一> -- IStartFilter IStartFilter 实现了Configure,如下图一,而Configure方法接受并返回Action< ...

随机推荐

  1. BZOJ_2006_[NOI2010]超级钢琴_贪心+堆+ST表

    BZOJ_2006_[NOI2010]超级钢琴_贪心+堆+ST表 Description 小Z是一个小有名气的钢琴家,最近C博士送给了小Z一架超级钢琴,小Z希望能够用这架钢琴创作出世界上最美妙的 音乐 ...

  2. BZOJ_2734_[HNOI2012]集合选数_构造+状压DP

    BZOJ_2734_[HNOI2012]集合选数_构造+状压DP 题意:<集合论与图论>这门课程有一道作业题,要求同学们求出{1, 2, 3, 4, 5}的所有满足以 下条件的子集:若 x ...

  3. 利用FT232实现USB转串口

    FT232B数据手册:http://www.ftdichip.com/Support/Documents/DataSheets/ICs/DS_FT232BL_BQ.pdf 常用的USB转串口的芯片有F ...

  4. 【已解决】通过Package或者Package+Activity启动应用

    有时很烦人,打开要启动的apk,通过adb命令(adb shell "dumpsys activity |grep Focuse") 获取到的应用包名 无法使用adb命令(adb ...

  5. 性能超前,详解腾讯云新一代Redis缓存数据库

    背景 当前内存数据库发展迅速,用户对于存储系统的要求也越来越高,为了满足各类业务场景的需要,腾讯云设计了新一代的内存数据库,不但保留了原来系统的高性能,高可用等特性,同时还兼容了当前流行的Redis原 ...

  6. Electron学习笔记(一)

    Electron是使用Javascript.HTML5技术构建跨平台桌面应用的技术,是目前非常活跃的一项技术,其中比较有名气的应用有微软的VS Code. 创建一个Electron应用的方式有很多,G ...

  7. Asp.NETCore轻松学系列阅读指引目录

    前言 耗时两个多月,坚持写这个入门系列文章,就是想给后来者更好更快的上手体验,这个系列可以说是从入门到进阶,适合没有 .NETCore 编程经验到小白同学,也适合从 .NET Framework 迁移 ...

  8. 深度学习与计算机视觉:基于Python的神经网络的实现

    在前面两篇文章介绍了深度学习的一些基本概念,本文则使用Python实现一个简单的深度神经网络,并使用MNIST数据库进行测试. 神经网络的实现,包括以下内容: 神经网络权值的初始化 正向传播 误差评估 ...

  9. 5.1基于JWT的认证和授权「深入浅出ASP.NET Core系列」

    希望给你3-5分钟的碎片化学习,可能是坐地铁.等公交,积少成多,水滴石穿,码字辛苦,如果你吃了蛋觉得味道不错,希望点个赞,谢谢关注. Cookie-Based认证 认证流程 我们先看下传统Web端的认 ...

  10. 软件开发架构介绍||OSI七层协议之物理层、数据链路层、网络层、传输层(mac地址、ip协议、断开协议、tcp协议之三次握手四次挥手)

    一.网络编程 软件开发架构 C/S架构 C:客户端 想体验服务的时候才会去找服务端体验服务 S:服务端   24小时不间断的提供服务,即时监听,随时待命 B/S架构 B:浏览器    想体验服务的时候 ...