原文链接:Limitations with Retrying Execution Strategies (EF6 onwards)

当使用重试执行策略的时候,大体有以下两种局限性:

不支持以流的方式进行查询

默认情况下,EF 6 或以后的版本,都是使用缓冲的方式而不是流的方式进行查询。如果你想使用以流的方式进行查询,可以使用 AsStreaming 方法来改变 LINQ to Entities 的默认查询方式:

using (var db = new BloggingContext())
{
var query = (from b in db.Blogs
orderby b.Url
select b).AsStreaming();
}

重试执行策略不支持以流的方式来进行查询,因为连接中可能会丢失掉部分的返回信息,而 EF 并不能确定哪些是已经正确返回了(数据可能在查询执行后被改变了,返回信息的顺序可能是不一定的,返回信息也可能没有一个唯一标识),所以无法进行有效的重试执行。

不支持用户手动启动的事务

当你配置了重试执行策略时,对于使用事务是有一定限制的。

怎样是被支持的:EF 默认的事务机制

默认情况下,EF 对数据库进行更新操作时,都会自动的建立在一个事务里,你无需额外的做些什么来开启它。

举个例子,下面代码中的 SavaChanges 会自动的执行在一个事务中,当插入数据中的任何一条失败,事务都会回滚,从而不会对数据库进行任何更改,这时上下文中也会有一个状态来允许 SavaChanges 进行重试操作。

using (var db = new BloggingContext())
{
db.Blogs.Add(new Site { Url = "http://msdn.com/data/ef" });
db.Blogs.Add(new Site { Url = "http://www.cnblogs.com/prinsun/" });
db.SaveChanges();
}

怎样是不被支持的:用户手动开启的事务

当不使用重试执行策略的时候,用户可以将多个操作包裹到一个事务中。如下面的代码中,在一个事务中包裹了两个 SavaChanges ,当其中任何一个失败,数据库中都不会有任何记录产生。

using (var db = new BloggingContext())
{
using (var trn = db.Database.BeginTransaction())
{
db.Blogs.Add(new Site { Url = "http://msdn.com/data/ef" });
db.Blogs.Add(new Site { Url = "http://www.cnblogs.com/prinsun/" });
db.SaveChanges(); db.Blogs.Add(new Site { Url = "http://twitter.com/efmagicunicorns" });
db.SaveChanges(); trn.Commit();
}
}

当使用重试执行策略的时候,这样的操作是不受支持的。因为 EF 无法知道任何先前的操作,以至于无法去重试它们。比如,如果上诉的第二个 SavaChanges 失败了,EF 没有办法去尝试重新执行第一个 SavaChanges。

变通的方式

挂起执行策略

一种可行的变通方案便是在需要手动开启事务的时候,暂时的挂起重试执行策略。最简单的实现方法便是在执行策略的配置类中加入一个是否挂起的标识,并在执行策略的 lambda 表达式中用该标识做一个判定:

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.SqlServer;
using System.Runtime.Remoting.Messaging; namespace Demo
{
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
this.SetExecutionStrategy("System.Data.SqlClient", () => SuspendExecutionStrategy
? (IDbExecutionStrategy)new DefaultExecutionStrategy()
: new SqlAzureExecutionStrategy());
} public static bool SuspendExecutionStrategy
{
get
{
return (bool?)CallContext.LogicalGetData("SuspendExecutionStrategy") ?? false;
}
set
{
CallContext.LogicalSetData("SuspendExecutionStrategy", value);
}
}
}
}

注意,我们使用了 CallContext 来存储这样的一个标识,这是为了这个值能和调用线程有关,能够安全的支持异步或多线程下的查询(即不会影响到其它线程中的策略执行)。

现在我们可以在需要手动开启事务的时候,使用挂起策略的这个功能了:

using (var db = new BloggingContext())
{
MyConfiguration.SuspendExecutionStrategy = true; using (var trn = db.Database.BeginTransaction())
{
db.Blogs.Add(new Site { Url = "http://msdn.com/data/ef" });
db.Blogs.Add(new Site { Url = "http://www.cnblogs.com/prinsun/" });
db.SaveChanges(); db.Blogs.Add(new Site { Url = "http://twitter.com/efmagicunicorns" });
db.SaveChanges(); trn.Commit();
} MyConfiguration.SuspendExecutionStrategy = false;
}

手动调用执行策略

另外一个选择便是手动的来执行重试策略,我们需要给相应的策略提供重试的执行逻辑,这样它才可以在操作失败时进行有效的重试。为了防止配置中的策略干扰,我们仍然需要将其挂起。

注意,所有的 Context 都需要在重试的代码块中进行实例化,这样才可以确保在每一次的重试操作中所有模型的状态是正确的:

var executionStrategy = new SqlAzureExecutionStrategy();

MyConfiguration.SuspendExecutionStrategy = true;

executionStrategy.Execute(
() =>
{
using (var db = new BloggingContext())
{
using (var trn = db.Database.BeginTransaction())
{
db.Blogs.Add(new Site { Url = "http://msdn.com/data/ef" });
db.Blogs.Add(new Site { Url = "http://www.cnblogs.com/prinsun/" });
db.SaveChanges(); db.Blogs.Add(new Site { Url = "http://twitter.com/efmagicunicorns" });
db.SaveChanges(); trn.Commit();
}
}
}); MyConfiguration.SuspendExecutionStrategy = false;

【EF 译文系列】重试执行策略的局限性(EF 版本至少为 6)的更多相关文章

  1. Entity Framework 6 暂停重试执行策略

    EF6引入一个弹性连接的功能,也就是允许重新尝试执行失败的数据库操作.某些复杂的场景中,可能需要启用或停用重试执行的策略,但是EF框架暂时尚未提供直接的设置开关,将来可能会加入这种配置.幸运的是,很容 ...

  2. 【EF 译文系列】韧性连接、重试(EF 版本至少为 6)

    原文链接:Connection Resiliency / Retry Logic (EF6 onwards) 一个应用程序的数据库连接,是非常容易受其它因素影响的,比如后端的异常或者不稳定的网络连接等 ...

  3. 【EF 译文系列】模型和数据库连接

    原文链接:Connections and Models 本篇文章主要包括 Entity Framework  是如何选择数据库进行连接,以及我们如何去改变它的连接.无论是通过 Code First 还 ...

  4. EF CodeFirst系列(6)---配置1对1,1对多,多对多关系

    这一节介绍EF CodeFirst模式中的1对0/1,1对多,多对多关系的配置,只有梳理清楚实体间的关系,才能进行愉快的开发,因此这节虽然很简单但是还是记录了一下. 1. 1对0/1关系配置 1. 通 ...

  5. EF CodeFirst系列(9)---添加初始化数据和数据库迁移策略

    1.添加初始化数据(Seed) 我们可以在初始化数据库的过程中给数据库添加一些数据.为了实现初始化数据(seed data)我们必须创建一个自定义的数据库初始化器(DB initializer),并重 ...

  6. 7.翻译系列:EF 6中的继承策略(EF 6 Code-First 系列)

    原文地址:http://www.entityframeworktutorial.net/code-first/inheritance-strategy-in-code-first.aspx EF 6 ...

  7. EF 学习系列三 数据操作数据加载及EF中执行Sql

    1.实体状态 我们通过EF来对数据库进行操作并持久化到数据库,那么EF必然通过EF上下文来维护实体的状态,明确知道每一个状态所对应的操作.也就是说EF通过上下文负责跟踪实体的状态.EF实体状态存在命名 ...

  8. 20.1翻译系列:EF 6中自动数据迁移技术【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/automated-migration-in-code-first.aspx EF 6 ...

  9. 16.翻译系列:EF 6 Code -First中使用存储过程【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/entityframework6/code-first-insert-update-delete-stored ...

随机推荐

  1. 记一次苦逼的SQL查询优化

    最近在维护公司项目时,需要加载某页面,总共加载也就4000多条数据,竟然需要35秒钟,要是数据增长到40000条,我估计好几分钟都搞不定.卧槽,要我是用户的话估计受不了,趁闲着没事,就想把它优化一下, ...

  2. parallels无法启动之大乌龙-流水账版

    欢迎访问我的blog:blog.thinkinside.me     早上到公司,像往日一样,开电脑倒茶喝水. 回到座位打开parallels desktop,发现不对,打开PD非常的慢.显示正在初始 ...

  3. Gson 和 FastJson 性能测试

    使用版本: compile 'com.google.code.gson:gson:2.7' compile 'com.alibaba:fastjson:1.2.17' 评测样板为一个People数组, ...

  4. 后台增加一个左侧列表菜单menu菜单的方法

    Ecshop 后台增加一个左侧列表菜单menu菜单需要修改三个文件:/admin/includes/inc_menu.php/admin/includes/inc_priv.php/languages ...

  5. 99 Lisp Problems 列表处理(P1~P28)

    L-99: Ninety-Nine Lisp Problems 列表处理类问题的解答,用Scheme实现,首先定义几个在后续解题中用到的公共过程: ; common procedure (define ...

  6. SOA架构改造简单记录

    前端支持PC.Mobile.H5三个平台 nginx做负载均衡,主备机,keepalived,检测脚本,master和slave切换时完成相关工作: web做集群,web仅仅是web,与后端服务模块采 ...

  7. Android JNI框架图

  8. iOS开发之身份证号码校验

    // //  Card.h //  THCStore // //  Created by Mac on 15/7/15. //  Copyright (c) 2015年 Mac. All rights ...

  9. [英] 推荐 15 个 jQuery 选择框插件

    jQuery Selectbox Plugins let you create beautiful and eye catching select box for your websites inst ...

  10. iOS SDK Release Notes for iOS 9 iOS9 SDK 版本更新说明

    Important: This is a preliminary document for an API or technology in development. Apple is supplyin ...