基于上篇文章《HiBlogs》重写笔记[1]--从DbContext到依赖注入再到自动注入园友 @Flaming丶淡蓝@ 吴瑞祥 提出了讨论和质疑,吓得我连夜查询资料(玩笑~)。

本来重点是想分析“自动注入”和对“注入”有更深的理解。不过既然有疑问和讨论那也是很好的。总比时不时来篇“这个不行”“那个要死了”的好。

之所以没有在评论区马上回复,是因为我确实不懂。所以下班后赶紧查阅相关资料。

我个人得出来的结论是:DbContext可以单次请求内唯一,且可以不主动释放。(其实当时心里也纳闷了。asp.net core就是这么干的啊,如果有问题还玩个毛线啊)

相关资料:http://blog.jongallant.com/2012/10/do-i-have-to-call-dispose-on-dbcontext/

这篇资料博客应该还是有一定的权威性的,内容是EF团队解释回应。

Hello Jon,
The default behavior of DbContext is that the underlying connection is automatically opened any time is needed and closed when it is no longer needed. E.g. when you execute a query and iterate over query results using “foreach”, the call to IEnumerable<T>.GetEnumerator() will cause the connection to be opened, and when later there are no more results available, “foreach” will take care of calling Dispose on the enumerator, which will close the connection. In a similar way, a call to DbContext.SaveChanges() will open the connection before sending changes to the database and will close it before returning.
Given this default behavior, in many real-world cases it is harmless to leave the context without disposing it and just rely on garbage collection.
That said, there are two main reason our sample code tends to always use “using” or dispose the context in some other way:
1. The default automatic open/close behavior is relatively easy to override: you can assume control of when the connection is opened and closed by manually opening the connection. Once you start doing this in some part of your code, then forgetting to dipose the context becomes harmful, because you might be leaking open connections.
2. DbContext implements IDiposable following the recommended pattern, which includes exposing a virtual protected Dispose method that derived types can override if for example the need to aggregate other unmanaged resources into the lifetime of the context.
By the way, with DbContext the pattern to open the connection manually and override the automatic open/close behavior is a bit awkward:
((IObjectContextAdapter)dbContext).ObjectContext.Connection.Open()
But we have a bug to make this easier as it used to be with ObjectContext before, e.g.:
dbContext.Database.Connection.Open()
Hope this helps,
Diego

谷歌翻译如下(英文不行,不知道翻译是否正确):

乔恩,
DbContext的默认行为是底层连接在需要时自动打开,并在不再需要时关闭。例如,当您执行查询并使用“foreach”迭代查询结果时,对IEnumerable <T> .GetEnumerator()的调用将导致打开连接,并且稍后再没有可用的结果,“foreach”将会关闭调用Dispose在枚举器上,这将关闭连接。以类似的方式,调用DbContext.SaveChanges()将在将更改发送到数据库之前打开连接,并在返回之前关闭它。
鉴于这种默认行为,在许多现实世界的情况下,离开上下文而不处理它,只依靠垃圾回收是无害的。
也就是说,我们的示例代码往往总是使用“使用”或以其他方式处理上下文的两个主要原因:
1.默认的自动打开/关闭行为相对容易被覆盖:您可以通过手动打开连接来控制何时打开和关闭连接。一旦您在代码的某些部分开始执行此操作,那么忘记使用上下文会变得有害,因为您可能会泄露打开的连接。
2.DbContext根据推荐的模式实现IDiposable,其中包括暴露一个虚拟保护的Dispose方法,如果需要将其他非托管资源聚合到上下文的生命周期中,派生类型可以覆盖。
顺便说一下,用DbContext打开手动连接的模式,覆盖自动打开/关闭的行为有点尴尬:
((IObjectContextAdapter)的DbContext).ObjectContext.Connection.Open()
但是,我们有一个错误,使之更容易,因为它曾经与ObjectContext之前,例如:
dbContext.Database.Connection.Open()
希望这可以帮助,
迭戈

光说不练假把式,我们还是亲自来测试一下吧。

我们测试分两种情况:

  • 1、主动释放DbContext
  • 2、不释放DbContext
  • 3、最好能用多线程模拟下并发
  • 4、然后查看执行时数据库的连接数,和程序执行完之后数据库的连接数。

测试代码:

//模拟数据库的一些操作(为了相对真实,包含了新增、修改和查询)
private static void DbOperation(BloggingContext db)
{
db.Blogs.Add(new Blog()
{
Rating = 1,
Url = "www.i.haojima.net"
});
db.SaveChanges(); db.Blogs.First().Url = "www.haojima.net";
db.SaveChanges(); foreach (var item in db.Blogs.Take(10).ToList())
{
Console.WriteLine("查询到的博客id:" + item.BlogId);
}
}

条件输入:

static void Main(string[] args)
{
Console.WriteLine("是否主动释放DbContext(y/n)");
var yes = Console.ReadLine();
Console.WriteLine("请输入模拟并发量");
var number = Console.ReadLine();
SemaphoreSlim _sem = new SemaphoreSlim(int.Parse(number));

循环代码:

var i = 0;
while (i <= 5000)
{
Console.WriteLine("启动第" + i++ + "个线程"); _sem.Wait(); #region Thread
new Thread(() =>
{
if (yes == "y")
{
using (BloggingContext bloggingContext = new BloggingContext())//主动释放
{
DbOperation(bloggingContext);
}
}
else
{
BloggingContext bloggingContext = new BloggingContext();//不释放
DbOperation(bloggingContext);
} }).Start();
#endregion _sem.Release();

查看连接数量(sql语句):

SELECT count(1) AS '连接到EFCoreDemoDB2数据库的数量' FROM
[Master].[dbo].[SYSPROCESSES] WHERE [DBID] IN ( SELECT
[DBID]
FROM
[Master].[dbo].[SYSDATABASES]
WHERE
NAME='EFCoreDemoDB2'
)

操作截图如下(你也可以下载demo代码自行测试):

主动释放、模拟200并发量



数据库看到的连接数最多的时候54个

不释放、模拟200并发量

数据库看到的连接数最多的时候56个

程序执行完成后,连接自动释放了

 

【技巧】:

我们使用ef或dbcontext的时候主要注意三个问题:

  • 1、多个线程不能访问同一个dbcontext
  • 2、同一个跟踪实体不能被多个dbcontext操作
  • 3、如果查询数据不需要被修改,一定按需查询.select(t=>new Dto(){ })。最不济也要AsNoTracking().ToList()。

    一般也就不会出现奇怪的问题了。

 

【注意】运行测试的时候用命令行执行或者“开始执行不调试”

demo:https://github.com/zhaopeiym/BlogDemoCode/tree/master/EFCoreDemo

当然,我也不知道这种测试是否合理。如果园友有更好的测试方式可以提供。欢迎大家交流。

关于DbContext能不能单次请求内唯一?DbContex需不需要主动释放?欢迎各路大侠来“参战”!的更多相关文章

  1. JDK HttpClient 单次请求的生命周期

    HttpClient 单次请求的生命周期 目录 HttpClient 单次请求的生命周期 1. 简述 2. uml图 3. Http连接的建立.复用和降级 3.1 调用流程及连接的建立和复用 3.2 ...

  2. html5-5 HTML5表单元素和内嵌框架

    html5-5   HTML5表单元素和内嵌框架 一.总结 一句话总结:单选框和多选框选的时候外面加label就可以实现选后面文字也可以选中了 1.html5如何实现文件上传? 必须加上enctype ...

  3. 清除表单input输入框内数据

    清除表单input输入框内数据 1. $(':input','#addVoucherType') //'#addVoucherType'表单id .not(':button') .val('') .r ...

  4. EF 保证线程内唯一 上下文的创建

    1.ef添加完这个对象,就会自动返回这个对象数据库的内容,比如下面这个表是自增ID 最后打印出来的ID  就是自增的结果 2.lambda 中怎么select * var userInfoList = ...

  5. C# 如何保证对象线程内唯一:数据槽(CallContext)

    如果说,一个对象保证全局唯一,大家肯定会想到一个经典的设计模式:单例模式,如果要使用的对象必须是线程内唯一的呢? 数据槽:CallContext,ok看下msdn对callcontent的解释. Ca ...

  6. C# 如何保证对象线程内唯一:数据槽(CallContext)【转载】

    如果说,一个对象保证全局唯一,大家肯定会想到一个经典的设计模式:单例模式,如果要使用的对象必须是线程内唯一的呢? 数据槽:CallContext,ok看下msdn对callcontent的解释. Ca ...

  7. EF上下文对象创建之线程内唯一

    在一次请求中,即一个线程内,若是用到EF数据上下文对象,就创建一个,那么会造成数据混乱,每次创建的对象执行相应的数据库操作,此同时,其他的EF对象内获得的数据可能已经是“过期”的了.即这个数据已经变动 ...

  8. 如何保证对象线程内唯一:数据槽(CallContext)

    CallContext 是类似于方法调用的线程本地存储区的专用集合对象,并提供对每个逻辑执行线程都唯一的数据槽.数据槽不在其他逻辑线程上的调用上下文之间共享.当 CallContext 沿执行代码路径 ...

  9. 关于EFCore线程内唯一

    EntityFramework的线程内唯一 EntityFramework的线程内唯一是通过httpcontext来实现的 public static DbContext DbContext() { ...

随机推荐

  1. windows7下VirtualBox COM获取对象失败

    一.问题 今天在windows7系统下安装virtualbox报错:VirtualBox COM获取对象失败,应用程序将被中断.上网查了下,发现了解决办法. 环境:windows7.VirtualBo ...

  2. open文件操作之mode模式剖析

    Python可以使用open函数来实现文件的打开,关闭,读写操作: Python3中的open函数定义为: open(file, mode='r', buffering=None, encoding= ...

  3. python自学1——接口测试

    尝试写了一个简单的接口测试,基于Python3.4,主要用到了Python读取excel以及requests库的知识,也算是对这段时间Python基础知识学习的一个巩固吧. 因为还没有学习到Pytho ...

  4. CSS3笔记之第三天

    CSS浮动 float:right 伪类: a:link {color:#FF0000;}      /* 未访问链接*/ a:visited {color:#00FF00;}  /* 已访问链接 * ...

  5. Thread.interrupt()方法理解

    原博客地址: 多线程编程 实战篇 (四) 不客气地说,至少有一半人认为,线程的"中断"就是让线程停止. 如果你也这么认为,那你对多线程编程还没有入门. 在java中,线程的中断(i ...

  6. 一次由SELinux引起的ssh公钥认证失败问题

    一直使用CentOS作为服务器系统,平时装完系统以后都是建立publickey认证机制,然后关闭密码认证.原本是一件轻车熟路毫无压力的事情,不想前日新装一台机器按照正常配置以后居然使用publicke ...

  7. 使用Jmeter进行http接口测试 ---------成都杀手

    前言: 本文主要针对http接口进行测试,使用Jmeter工具实现. Jmter工具设计之初是用于做性能测试的,它在实现对各种接口的调用方面已经做的比较成熟,因此,本次直接使用Jmeter工具来完成对 ...

  8. 递归的二叉查找树Java实现

    package practice; public class TestMain { public static void main(String[] args) { int[] ao = {50,18 ...

  9. C++中关于重载默认构造函数与默认全部参数的构造函数的使用注意

    # include<iostream>using namespace std;class Time{public:                            //公用成员函数  ...

  10. C#将Excel数据表导入SQL数据库的两种方法(转)

    最近用写个winform程序想用excel 文件导入数据库中,网上寻求办法,找到了这个经过尝试可以使用. 方法一: 实现在c#中可高效的将excel数据导入到sqlserver数据库中,很多人通过循环 ...