Entity Framework context per request

原文发布时间为: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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/429067.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java commit()_Java XAResource.commit方法代码示例

import javax.transaction.xa.XAResource; //导入方法依赖的package包/类public void testSuspendableTx() throws Exception {if (!versionMeetsMinimum(5, 0)) {return;}Connection conn1 null;MysqlXADataSource suspXaDs new MysqlXADataSource();suspXaDs.setUrl(BaseTe…

python如何优雅判断类型

http://note.youdao.com/noteshare?id6f3a7963efc57b5d0b1c712654d100c6转载于:https://www.cnblogs.com/taiyang-li/p/7190195.html

java网络io_彻底搞懂Java的网络IO

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼java.io包基于流模型实现&#xff0c;提供File抽象、输入输出流等IO的功能。交互方式是同步、阻塞的方式&#xff0c;在读取输入流或者写入输出流时&#xff0c;在读、写动作完成之前&#xff0c;线程会一直阻塞。java.io包的好处是…

1. 在虚拟机中 添加内容

步骤&#xff1a; 1. 找到要添加的内容&#xff0c;按住 ctrl c 复制 &#xff0c;例如&#xff1a;复制 飞秋 2. 打开 虚拟机&#xff0c;找到 要复制文件的位置。 3. 将 复制的文件添加到 共享文件夹下面。 4. 打开虚拟机&#xff0c;安装飞秋 5&#xff0c; 最后就完成了…

JavaScript设计模式-10.工厂模式实例xhr

1 <!DOCTYPE html>2 <html>3 <head>4 <meta charset"UTF-8">5 <title>Javascript高级语法10-工厂模式实例xhr</title>6 </head>7 <body>8 <script>9 //接口…

java web scala_spring boot+scala编写web接口

本人是Java开发者&#xff0c;有面向对象的基础&#xff0c;而Scala也是面向对象的语言&#xff0c;学习后可快速入门。通过学习Scala的面向对象(和java面向对象类似)、Scala的高级函数(map,reduce等&#xff0c;和Java8中的stream编程类似)、Scala的隐式转换(在Java中可通过sp…

java对象引用出错_上传图片错误:尝试在空对象引用上调用虚拟方法’java.lang.String android.net.Uri.getLastPathSegment()’...

我有“用相机捕获图像”的问题,并将其存储到Firebase中.我认为该代码是正确的,因为它可以与“从图库中选择图像”一起使用.捕获完图像后,该应用程序停止了,并且没有存储在数据库中.我认为这对于android M和N是个问题.我只是看到其他类似的问题,但它们对我不起作用.我为此寻求帮…

数组的运用

一、数组的概念以及定义&#xff1a; 1、 数组是存储同一种数据类型多个元素的集合&#xff0c;也可以看作是一个容器。 2、数组既可以存储基本数据类型&#xff0c;也可以存储引用数据类型。 二、一维数组&#xff1a; 1、一维数组的定义&#xff1a; 数据类型 [ ] 数组名 ne…

java小应用_java小应用

第一次使用简书记笔记&#xff0c;主要目的是为了加深印象&#xff0c;方便忘记时及时翻看。hello.java代码如下&#xff1a;import java.applet.Applet;import java.awt.*;public class hello extends Applet{private Font f1;public void init(){f1 new Font("宋体&quo…

POJ 2288 Islands and Bridges(状压dp)

http://poj.org/problem?id2288 题意&#xff1a; 有n个岛屿&#xff0c;每个岛屿有一个权值V&#xff0c;一条哈密顿路径C1&#xff0c;C2&#xff0c;...Cn的值为3部分之和&#xff1a; 第1部分&#xff0c;将路径中每个岛屿的权值累加起来&#xff1b;第2部分&#xff0c;对…

java做橡皮擦效果_HTML5 canvas橡皮擦擦拭效果

这是一款HTML5 canvas橡皮擦擦拭效果。该效果通过canvas来制作遮罩层和擦拭用的橡皮擦&#xff0c;用户可以通过移动鼠标来移除遮罩层&#xff0c;效果非常炫酷。因为发代码有时会排版混乱&#xff0c;所以先发图演示了。源码已经打包好了&#xff0c;想学习的朋友可以下载练习…

web前端开发面试题(答案)

1.xhtml和html有什么区别&#xff1f; HTML是一种基本的WEB网页设计语言&#xff0c;XHTML是一个基于XML的置标语言最主要的不同&#xff1a;XHTML 元素必须被正确地嵌套。XHTML 元素必须被关闭。标签名必须用小写字母。XHTML 文档必须拥有根元素。 2.行内元素有哪些?块级元素…

mysql to mssql_MysqlToMsSql

MysqlToMsSql是一款简单易用的数据库迁移工具&#xff0c;这款软件功能强大&#xff0c;可以帮助用户将MySQL数据库内容转移到msSQL数据库中&#xff0c;采用可视化操作&#xff0c;支持预览&#xff0c;方便用户查看数据库&#xff0c;实用性强&#xff0c;有需要的用户快来下…

java延迟覆盖_高效Java第九条覆盖equals时总要覆盖hashCode

原标题&#xff1a;高效Java第九条覆盖equals时总要覆盖hashCode高效Java第九条覆盖equals时总要覆盖hashCode在每个覆盖了equals方法的类中&#xff0c;也必须覆盖hashCode方法。否则会导致该类无法与基于散列的集合一起正常运作。 hashCode约定在应用程序的执行期间&#xff…

原生js简单实现双向数据绑定原理

根据对象的访问器属性去监听对象属性的变化&#xff0c;访问器属性不能直接在对象中设置&#xff0c;而必须通过 defineProperty() 方法单独定义。 访问器属性的"值"比较特殊&#xff0c;读取或设置访问器属性的值&#xff0c;实际上是调用其内部特性&#xff1a;get…

Flyweight Design Pattern 共享元设计模式

就是利用一个类来完毕多种任务&#xff0c;不用每次都创建一个新类。 个人认为这个设计模式在C里面。好像能够就使用一个函数取代&#xff0c;利用重复调用这个函数完毕任务和重复利用这个类&#xff0c;好像差点儿相同。 只是既然是一个设计模式&#xff0c;那么就使用类来完毕…

java中write方法报错_Java中管道报错:Write end dead

今天看了下关于管道的通信&#xff0c;Java中的管道只能在同一进程的不同线程间通信。今天测试两个线程进行通信发现报错。下面是我测试的代码。package com.wpl.testIO;import java.io.IOException;import java.io.PipedInputStream;import java.io.PipedOutputStream;public …

Linux课堂笔记-第二天

Shell简介 在Linux系统中&#xff0c;shell是最常用程序&#xff0c;作用侦听用户命令&#xff0c;启动命令所指定的进程并将结果返回给用户。他还是一种程序设计语言&#xff0c;是系统维护时的重要工具。 两部分组成&#xff0c;#为命令行提示符&#xff1b;#为超级用户&…

java多属性的map_java - 具有多个参数的MapStruct QualifiedByName - 堆栈内存溢出

目前&#xff0c;MapStruct不支持具有多个源属性的映射方法。但是&#xff0c;在您的情况下&#xff0c;您可以使用1.2.0中的Context 。 根据我的理解&#xff0c; projId和code就像映射的帮助器一样&#xff0c;它们不用于映射目标属性。所以你可以做一些事情(它应该在理论上起…

用带参数的方法给空数组放元素,寻找数组里面的值是否存在。

package ch08;import java.util.Scanner;/** * Created by liwenj on 2017/7/20. */public class Person { String[] names new String[30];//把为null的元素添加内容 void addName(String name) { for (int i 0; i < names.length; i) { if (nam…