数据加载

如下这样的一个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. ajax封装函数和表单序列化

    //表单序列化function iSerialize(form){ var parts={}; for(var i=0;i<form.elements.length;i++){ var file ...

  2. 记一次logback传输日志到logstash根据自定义设置动态创建ElasticSearch索引

    先说背景,由于本人工作需要创建很多小应用程序,而且在微服务的大环境下,服务越来越多,然后就导致日志四分五裂,到处都有,然后就有的elk,那么问题来了 不能每个小应用都配置一个 logstash 服务来 ...

  3. 张高兴的 Windows 10 IoT 开发笔记:串口红外编解码模块 YS-IRTM

    This is a Windows 10 IoT Core project on the Raspberry Pi 2/3, coded by C#. GitHub: https://github.c ...

  4. json数组的解析

    一直以来,经常会遇到json数据从前端或者我经常从网站上爬取的数据中会有json数据的存在,这样如果想要获取json数据就需要对json数据进行解析 在开发过程中,经常需要和别的系统交换数据,数据交换 ...

  5. 关于DatePicker在模态窗体下失效的问题

    最近用bootstrap做了一个租赁相关的管理系统,由于前端知识薄弱,也是编查资料边做.关于一些控件的用法,也是从网上查资料.下面,来说一下在写前端页面时遇到的几个坑. 这个系统中,日期控件用的是Da ...

  6. Go中原始套接字的深度实践

    1. 介绍 2. 传输层socket 2.1 ICMP 2.2 TCP 2.3 传输层协议 3. 网络层socket 3.1 使用Go库 3.2 系统调用 3.3 网络层协议 4. 总结 4.1 参考 ...

  7. 基于JavaMail开发邮件发送器工具类

    基于JavaMail开发邮件发送器工具类 在开发当中肯定会碰到利用Java调用邮件服务器的服务发送邮件的情况,比如账号激活.找回密码等功能.本人之前也碰到多次这样需求,为此特意将功能封装成一个简单易用 ...

  8. shiro的SecurityUtis

    接着上一篇来继续分析shiro源码 这篇主要讲解shiro里面的SecurityUtils 首先我们看该类供我们在业务中用的仅有两个get方法,那么这两个get方法获取的subject和sercuri ...

  9. 零基础如何学Python爬虫技术?

    在作者学习的众多编程技能中,爬虫技能无疑是最让作者着迷的.与自己闭关造轮子不同,爬虫的感觉是与别人博弈,一个在不停的构建 反爬虫 规则,一个在不停的破译规则. 如何入门爬虫?零基础如何学爬虫技术?那前 ...

  10. python接口自动化(二十一)--unittest简介(详解)

    简介 前边的随笔主要介绍的requests模块的有关知识个内容,接下来看一下python的单元测试框架unittest.熟悉 或者了解java 的小伙伴应该都清楚常见的单元测试框架 Junit 和 T ...