原文发布时间为:2011-09-24 —— 来源于本人的百度文章 [由搬家工具导入]

http://www.blog.cyberkinetx.com/2011/05/15/entity-framework-context-per-request/

Are you still using the

?1234using(DBContext context = newDBContext()){  ..}

way of accessing the database? I mean that’s cool, at least you are sure you don’t have connection pooling penalties. But where do you write this code? Do you have a Data Access Layer? How about accessing the Navigation Properties outside the using block? How about assuring a developer does not forget about the context restrictions and tries to access a related table outside the context?

I’ve seen developers creating the context inline without a using block, and then not bothering about disposing it, leaving this “monkey business” to the garbage collector. Because it was a web service project with a lot of access I volunteered to fix it, and encapsulate all contexts in using blocks. I didn’t bother to test it.. I mean what could go wrong, right? It was the DAL. Because of a combination of unfortunate events, the code found it’s way to live and crashed.. Somebody was using a Navigation Property in the Business Layer. The only reason why it did not crash before was the garbage collector, it was not disposing the context fast enough, so you could still write some code in the business before the context was disposed. A race against time actually..

On the next project, my first idea was to make all Navigation Properties in the entities as internal only to the DAL. But then cam the architect suggesting we use linq on the entities from the BL. That means the Entity Framework context must continue hes existence at the Business Layer (but not accessible). The solution? Unit Of Work.

First of all we created the Data Access Layer. Entity Framework as a concept is a DAL by itself, but we needed to separate it from our code for the sake of UT simplicity and because it simply is a good practice. It’s a lot simple to mock an interface than the EF. Inside the DAL project we created the Repository class that did all the DB access job. Actually it has only CRUD operations. For example:

?0102030405060708091011publicIQueryable Customers { get{ ... } } publicvoidAdd(Customer customer){  ..} publicvoidDelete(Object entity){  ..}

You may argue this are not CRUD because there is not Update operation. But because the Customer is exposed by the Customers property it can be updated by anybody outside the repository, and so it does expose the Update operation.

I skipped the implementation details as you are not ready to see it. All at it’s time. Let’s see what this CRUD operations give us. We have a repository that doesn’t have for ex. GetCustomerOwingMoney(), or GetNewOrders(). From a OOP point of view that would be operations on the Customer collection and Order collections. It does make sens not to write them in our BL. Very well then, let’s write them on the IQueryable<Customer> collection:

?1234publicIQueryable<Customer> OwingMoney(thisIQueryable<Customer> customers){  returncustomers.Where(x => x.Balance < 0);}

Hey, that’s an extension method! We followed the OOP way and got ourselves a collection of customers with a OwingMoney() method on it. Ain’t that awesome? As part of our project we created extension methods for all entities that needed operations on the collections (before putting just any method as an extension think first if it’s a BL related or it’a an operation related to that particular type). As a convention the classes with extension methods are called CustomerExtensions, OrderExtensions, etc. Pushing that forward, we also have partial classes for the entities with properties and methods, like OwsMoney (taking the previous example), or Suspend() to suspend a customer if he refuses to pay. So the extensions can be used from the BL in a kind of fluent way.

A question that might arise at this point is – if we have all this logic in the extension method and in the partial classes, what do we actually put in the BL? And indeed there is no thin border where the extension methods end and the BL starts. So far our rule is to put all the reusable code where possible to the extension methods and partial classes, the rest would be the BL.  For example, let’s say we need a method to suspend all customers that owe money to the shop, and have more than 2 open orders (i guess it has no logic but just for the sample sake), that can be done as an extension on the IQueryable<Customer>, but it will not be reused anywhere as it’s an action that will be triggered by some scheduled process. So it makes a lot of sense to write it in the BL:

?123456publicvoidSuspendBadCustomers(){  repository.Customers.OwingMoney()    .Where(x => x .Orders.OpenOrders.Count() > 2).ToList()    .ForAll(x => x.Suspend());}

Does that make sense? Not with a classic repository. As far we didn’t save the changes. We can just leave it like that. It will be saved.. eventually. And how in the world we dispose the context?? Oh well, we have the UnitOfWork for that.

Before I start discussing the UnitOfWork implementation I will assume you are familiar with Dependency Injection and Inversion of Control as explaining them is out of the scope of this post.

For the current post I will do a very simple UnitOfWork just to satisfy the problem of having a context persisted along the lifetime of the HTTP Request. More complext implementation would mean supporting multiple contexts, supporting different object persistence on the UnitOfWork.

?01020304050607080910111213141516171819202122232425publicclassUnitOfWork : IUnitOfwork, IDisposable{  publicObjectContext Context { get; set; }  publicDbTransaction Transaction { get; set; }   publicvoidCommit()  {    if(Context != null)    {      ObjectContext.SaveChanges();      Transaction.Commit();    }  }   publicvoidDispose()  {    if(Context != null)    {      Transaction.Dispose();      Transaction = null;      Context.Dispose();      Context = null;    }  }}

Here we have 2 properties for Context and another for Transaction. Because of the per request behavior, we can’t use the TransactionScope any more, so we’ll go with a bit old fashion way of working with transactions.

Next step would be to configure the IoC container to treat IUnitOfWork with a lifetime that would give the same instance for a HTTP Request. Meaning, whenever I’ll call my IoC like

?1IUnitOfWork unit = IoCContainer.Resolve<IUnitOfWork>();

I will be getting the same instance of the UnitOfWork in a single HTTP Request (to not be confused with HTTP Session).

Next step is to configure the Global.asax to handle the UnitOfWork, committing it when the request ends, and just disposing it when an exception is thrown so the transaction will be rolled back. What you need to add to the Global.asax:

?01020304050607080910111213publicvoidApplication_EndRequest(Object sender, EventArgs e){  IUnitOfWork unit =  IoCContainer.Resolve<IUnitOfWork>();  unit.Commit();  unit.Dispose();} publicvoid Application_Error(Object sender, EventArgs e){  IUnitOfWork unit =  IoCContainer.Resolve<IUnitOfWork>();  unit.Dispose();  //don't forget to treat the error here}

No actions are required on BeginRequest event. But so far the Entity Framework context isn’t initialized anywhere. It would make sense to initialize the context only when required. Some request might not hit the DB so why the extra penalty? Because I don’t want my BL to know much about EF I decided to do the initialization in Repository. I created a GetContext() method that returns the context whenever it is required. And because dependency injection is used the UnitOfWork can be set up as a parameter in the constructor and it will be injected when the Repository is instantiated (preferably by IoC as well):

?0102030405060708091011121314151617181920212223publicclassRepository : IRepository{  privateIUnitOfWork unitOfWork;   publicRepository(IUnitOfWork unitOfWorkk)  {    this.unitOfWork = unitOfWork;  }   //CRUD operations code would be here  //..   privateOurDbContext GetContext()  {    if(unitOfWork.Context == null)    {      unitOfWork.Context = newOurDbContext();      unitOfWork.Transaction = context.Connection.BeginTransaction();    }     return(OurDbContext)unitOfWork.Context;  }}

We are almost there. Just have to update our CRUD operations example, with the GetContext():

?0102030405060708091011publicIQueryable<Customer> Customers { get{ returnGetContext().Customers; } } publicvoidAdd(Customer customer){  GetContext().Customers.Add(customer);} publicvoidDelete(Object entity){  GetConetxt().DeleteObject(entity);}

We are there. Let’s summarize what we got. We have a UnitOfWork that is a-kind-of-singleton, that is retrieved using IoC, and will have the same instance as long it’s in the context of the same HTTP Request. In the repository whenever the first db operations is called a context and a transaction is created and saved on the UnitOfWork. The context will be reused in the repository as long as it’s doing operations for the same HTTP Request. Whenever the HTTP Request ends (a HTTP Reply is issued to the client), in case of no exceptions the transaction will be committed and all changes will be saved to the database, in case of exceptions the transaction will be reverted and a nice message must be issued to the end user, and a error log should be created. On the next request another UnitOfWork is created and another context.

For the Business Layer, we have safe access to the Navigation Properties, extension methods can be used for any entities. Performance increases because BL does not access repository methods for different actions creating new instances of the ObjectContext.

I’ve been asked if keeping a context for so long isn’t a performance issue by itself. The answer would be “depends”. If you are building a web site like Facebook you’ll probably search for other options (and most probably give up on EF). The pool size for the context has a default size of 100. That means you can have 100 requests that are processed in parallel, the 101 will wait for a context to be free. When you get to more then 100 parallel requests you’ll probably have other bottlenecks to worry about, and will already be thinking about load balancing.

If I may return to the problem of SaveChanges(). In the presented examples it is never called from Repository or BL as the examples are fairly simple. In more complicated scenarios it will be necessary so in our implementation we have a Save() method implemented in the repository that simply calls the SaveChanges() on the context. A scenario when you need it would be when you add to repository new entities, and then do a Where() and expect to find them. Well.. if you want’ save them before the Where() operation, you won’t.. That’s how EF works. Same with the changes. So if you need to add/update something and use that in the next query, do a save first. That has been one of my most common mistakes…

I did not treat the problem of lazy loading in this article as it’s outside the scope but when you start working with EF and use a lot of Navigation Properties keep this problem in mind.

转载于:https://www.cnblogs.com/handboy/p/7182595.html

Entity Framework context per request相关推荐

  1. 创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表

    创建ASP.NET Core MVC应用程序(3)-基于Entity Framework Core(Code First)创建MySQL数据库表 创建数据模型类(POCO类) 在Models文件夹下添 ...

  2. Entity Framework在Asp.net MVC中的实现One Context Per Request(附源码)

    上篇中"Entity Framework中的Identity map和Unit of Work模式", 由于EF中的Identity map和Unit of Work模式,EF体现 ...

  3. AppBox升级进行时 - 拥抱Entity Framework的Code First开发模式

    AppBox 是基于 FineUI 的通用权限管理框架,包括用户管理.职称管理.部门管理.角色管理.角色权限管理等模块. 从Subsonic到Entity Framework Subsonic最早发布 ...

  4. Entity Framework 6 Recipes 2nd Edition(9-1)译-用Web Api更新单独分离的实体

    第九章 在N层结构的应用程序中使用EF 不是所有的应用都能完全地写入到一个单个的过程中(就是驻留在一个单一的物理层中),实际上,在当今不断发展的网络世界,大量的应用程序的结构包含经典的表现层,应用程, ...

  5. 自定义Unity对象生命周期管理集成ADO.NET Entity Framework

    在Unity中,从Unity 取得的实例为 Transient.如果你希望使用多线程方式,就需要在组成时使用lifecycle参数,这时候取出的组件就不再是同一个了.在Unity IOC中,它支持我们 ...

  6. Entity Framework 代码模板

    各种使用方式 EntityClient+EntitySQL string city = "London"; using (EntityConnection cn = new Ent ...

  7. ASP.NET Core 中的 ORM 之 Entity Framework

    目录 EF Core 简介 使用 EF Core(Code First) EF Core 中的一些常用知识点 实体建模 实体关系 种子数据 并发管理 执行 SQL 语句和存储过程 延迟加载和预先加载 ...

  8. 使用Entity Framework Core,Swagger和Postman创建ASP.NET Core Web API的分步指南

    目录 介绍 背景 第1步:创建一个新项目 第2步:添加模型类 第3步:使用Entity Framework Core 第4步:添加数据库上下文和控制器 步骤5:在Package Manager控制台中 ...

  9. Entity Framework 数据并发访问错误原因分析与系统架构优化

    本文主要记录近两天针对项目发生的数据访问问题的分析研究过程与系统架构优化,我喜欢说通俗的白话,高手轻拍 1. 发现问题 系统新模块上线后,使用频率较高,故在实际使用和后期的问题重现测试中,产生了一下系 ...

最新文章

  1. 又一个Jupyter神器,操作Excel自动生成Python代码
  2. 二进制与格雷码之间的转换的Verilog实现(更多一点的讨论)
  3. 背景建模之单高斯实现
  4. 熬之滴水穿石:JSP--HTML中的JAVA代码(6)
  5. Nginx负载均衡监控节点状态
  6. python多线程下载器_用 python 实现一个多线程网页下载器
  7. 使用IDM下载,不适用默认浏览器下载
  8. python使方法执行10次_Python提升程序性能的七个手段
  9. intel ssd toolbox 绿色单文件_你想要的大容量来了!影驰擎GA-E 16TB SSD上手:速度喜人-SSD,固态 ——快科技(驱动之家旗下媒体)-...
  10. CentOS7安装 MySQL主从集群
  11. 【Python数据分析】数据挖掘建模——分类与预测——回归分析
  12. 用自己的算法实现startsWith和endsWith功能
  13. js setInterval() 用法示例
  14. Linux桌面没有minidwep,ubuntu 12.04完整安装minidwep-gtk教程
  15. 使用node.js构建一个web服务器(适合自学)
  16. 阿里云计算acp认证报考条件及需要具备的知识
  17. 发布3天获推荐10w+,视频号内容出现新玩法?
  18. Java培训出身,今获阿里Android岗offer,大专学渣的“登天”之路!
  19. 网页文件是用html语言创建的文本文件,把txt文件变成html网页文件
  20. 【转载】sdcard中平添文件总是提示Failed to push the item(s)Failed to push XXXXX.txt on emulato...

热门文章

  1. foxitreadersdk 打开远程文件_一种最不为人知最简单最方便的用电脑操作手机上的文件...
  2. java1.8 新特性
  3. GPU版的tensorflow在windows上的安装时的错误解决方案
  4. yii model层操作总结
  5. XCode5添加新建类模板(Cocos2dx Template Class for Scene or Layer)
  6. HDU 2222 Keywords Search
  7. 使用默认Model Binding支持集合类
  8. pycharm中更新pip版本的问题
  9. 时隔多日,旧域名重用,niceyoo博客上线
  10. Android list转xml