Make the DbContext Ambient with UnitOfWorkScope(now named DbContextScope by mehdime)
The Entity Framework DbContext (or LINQ-to-SQL DataContext) is a Unit Of Work implementation. That means that the same DbContext should be used for all operations (both reading and writing) within a single web or service request. That means that there are a lot of different places where the DbContext have to be accessed. To avoid having to pass the DbContext around as a parameter, I’ve created a UnitOfWorkScope
that makes the DbContext ambient and easily accessible.
A common beginners problem when working with Entity Framework or LINQ-to-SQL is to have too short life times of the DbContext. A problem that I’ve seen many questions about on Stack Overflow is when questions are encapsulated in repositories or helper methods. Inside each method a new DbContext is created for that specific read. Later, when the returned entity has been updated and is to be saved the problem occurs. The entity should be saved using the same DbContext that once read it from the database to allow change tracking to work properly. Clearly, having separate DbContexts is a problem.
The first attempt to solve it is usually to pass the DbContext around. That only solves half the problem though, that of accessing it. The other half of the problem is to decide where to call SaveChanges
to persist the changes done. Calling it from every method making changes spoils the entire unit of work concept. Trusting the creator of the context to know when any of a myriad of called functions have made changes seems risky.
I’ve been looking for a better way to handle the DbContext and have come up with an ambient DbContext, using a UnitOfWorkScope
which is similar to TransactionScope
.
The main features of the UnitOfWorkScope
are:
- The first method in the call chain opening a
UnitOfWorkScope
creates an ambient DbContext. - Subsequent methods in the call chain utilizes the same DbContext.
- Changes are only saved if all participating scopes called
SaveChanges
- Read only mode is available, where data is read using the existing DbContext, but no changes need to be saved. This is useful for GetSomeThing methods that are used both for pure reading and for reading for update.
A Shared Query
A shared query uses the UnitOfWorkScope
, instead of creating a DbContext instance directly.
public static Entities.Car GetCar(int id) |
The reading purpose is used to mark that this unit of work scope will not do any updates, so SaveChanges
will not be called. The method can be used both standalone to read data, or to fetch data for subsequent update. Let’s look at using it for updating.
using (var uow = new UnitOfWorkScope<CarsContext>(UnitOfWorkScopePurpose.Writing)) |
Here, the scope is opened for writing. Leaving the scope without calling SaveChanges
indicates an error, just like Complete
works on TransactionScope
.
Experiences of Using the UnitOfWorkScope
We’re using the unit of work scope in my current project. The experience so far is that we’ve removed the need to pass around DbContext instances all over. It is also much easier to reuse the same common set of base queries for reading and for writing. Any method can easily get hold of the ambient scope. A business logic method on an entity can a service method, which can now be part of the same unit of work without having to pass the DbContext around.
The UnitOfWorkScope code
The code makes use of the Disposable
base class, that I’ve written about before. The code is also available on GitHub.
/// <summary> |
The code has been updated with a better way to handle when a writing scope is opened to a reading root scope. In this version, an exception is thrown when the child scope is opened. Previously the exception was thrown when the root scope was disposed.
The code is written to be easy to use right and hard to use wrong. There is a risk that someone would call SaveChanges
directly on the wrapped DbContext – possibly saving changes early. To avoid that there is a guard which disallows that.
The code is thread safe as it uses thread local storage to store the current scope. However it will probably not work with async/await as it does not consider the synchronization context but rather is tied to the thread directly.
from:https://coding.abel.nu/2012/10/make-the-dbcontext-ambient-with-unitofworkscope/
Make the DbContext Ambient with UnitOfWorkScope(now named DbContextScope by mehdime)的更多相关文章
- Managing DbContext the right way with Entity Framework 6: an in-depth guide by mehdime
UPDATE: the source code for DbContextScope is now available on GitHub: DbContextScope on GitHub. A b ...
- DbContextScope,A simple and flexible way to manage your Entity Framework DbContext instances,by mehdime
DbContextScope A simple and flexible way to manage your Entity Framework DbContext instances. DbCont ...
- EF6 Database First (DbContext) - Change Schema at runtime
Problem:There are two SQL databases (dev and live) with on Azure which has identical table structure ...
- 在EntityFramework6中管理DbContext的正确方式——3环境上下文DbContext vs 显式DbContext vs 注入DbContext(外文翻译)
(译者注:使用EF开发应用程序的一个难点就在于对其DbContext的生命周期管理,你的管理策略是否能很好的支持上层服务 使用独立事务,使用嵌套事务,并行执行,异步执行等需求? Mehdi El Gu ...
- 在EntityFramework6中管理DbContext的正确方式——4DbContextScope:一个简单的,正确的并且灵活的管理DbContext实例的方式(外文翻译)
(译者注:使用EF开发应用程序的一个难点就在于对其DbContext的生命周期管理,你的管理策略是否能很好的支持上层服务 使用独立事务,使用嵌套事务,并行执行,异步执行等需求? Mehdi El Gu ...
- EF Core 2.0中Transaction事务会对DbContext底层创建和关闭数据库连接的行为有所影响
数据库 我们先在SQL Server数据库中建立一个Book表: CREATE TABLE [dbo].[Book]( ,) NOT NULL, ) NULL, ) NULL, ) NULL, [Cr ...
- MVC模式下unity配置,报错“No connection string named '**Context' could be found in the application config file”
写在前面: 第一次配置时好好的,后来第二次改到MVC模式,把依赖注入写成字典的单例模式时,由于新建的ORM(数据库映射模型EF),怎么弄都不用,一直报错"No connection str ...
- EntityFramework Core 1.1是如何创建DbContext实例的呢?
前言 上一篇我们简单讲述了在EF Core1.1中如何进行迁移,本文我们来讲讲EF Core1.1中那些不为人知的事,细抠细节,从我做起. 显式创建DbContext实例 通过带OnConfiguri ...
- Entity Framework 教程——DBContext
DBContext: 在之前的章节<创建实体数据模型>中,EDM为我们创建了SchoolDBEntities 类,它派生子System.Data.Entity.DbContext这个类,这 ...
随机推荐
- 关于Mysql5.6半同步主从复制的开启方法【转】
介绍 先了解一下mysql的主从复制是什么回事,我们都知道,mysql主从复制是基于binlog的复制方式,而mysql默认的主从复制方式,其实是异步复制. 主库实际上并不关心从库是否把数据拉完没有, ...
- PHP+mysql系统报错:PHP message: PHP Warning: Unknown: Failed to write session data (files)
PHP+mysql系统报错:PHP message: PHP Warning: Unknown: Failed to write session data (files) 故障现象,后台页面点击没有 ...
- MYSQL数据库链接层- SUMMER-SQL
2015年3月31日 18:27:34 最后编辑: 2016年4月17日 00:22:00 星期日 最后编辑: 2018-4-25 16:58:44 星期三 最新代码: https://gitee.c ...
- 2011TG初赛
一.单项选择题(共20题,每题1.5分,共计30分,每题有且仅有一个正确选项.) 1. 在二进制下,1011001+( )=1100110. A.1011 B.1101 C.1010 D.1111 B ...
- Carbon 的 diffForHumans 方法
Carbon 是继承自 PHP DateTime 类 的子类,但比后者提供了更加丰富.更加语义化的 API.其中一个比较实用的 API 就是 diffForHumans 方法,几乎每个用 Larave ...
- Servlet发送Http请求
今日遇到一个需求,android注册,短信验证码功能. android请求我服务端,我请求tosms.cn发送验证码短信给android,于是需要在Servlet中发送Http请求 package o ...
- Windows 10安装pip方法
pip是一款非常方便的python包管理工具,本文主要介绍在windows 10下安装pip方法. 1. 下载pip 地址:https://pypi.python.org/pypi/pip#downl ...
- ***解决UEditor编辑器无法插入第三方视频地址
转:http://blog.csdn.net/qq_16241043/article/details/53894847 xssFilter导致插入视频异常,编辑器在切换源码的过程中过滤掉img的_ur ...
- Cap+Exceptionless实现日志消息发布订阅异常情况日志处理及Cap DashBoard授权处理
Dashboard介绍 capOptions.UseDashboard(dashoptions => { dashoptions.AppPath = "applicationpath& ...
- Codeforces Round #533 (Div. 2) E - Helping Hiasat 最大团
E - Helping Hiasat 裸的最大团,写了一种 2 ^ (m / 2) * (m / 2)的复杂度的壮压, 应该还有更好的方法. #include<bits/stdc++.h> ...