基于上篇文章《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. PHPcms9.6.0任意文件上传漏洞直接getshell 利用教程

    对于PHPcms9.6.0 最新版漏洞,具体利用步骤如下: 首先我们在本地搭建一个php环境,我这里是appserv或者使用phpnow (官网下载地址:http://servkit.org/) (只 ...

  2. Html5笔记之第五天

    Html5增强的文件上传域 <!DOCTYPE html> <html> <head> <title>Html5增强的文件上传域</title&g ...

  3. 编写JsonResult封装JSON返回值(模板参阅)

    编写JsonResult封装JSON返回值 package cn.tedu.note.util; import java.io.Serializable; import cn.tedu.note.se ...

  4. SpirngMVC入门第一天

                                         SpringMVC第一天 1.   计划 第一天 1.SpringMVC介绍 2.入门程序 3.SpringMVC架构讲解 a ...

  5. js操作cookie 使用详解

    详见: http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp62  JavaScript中的另一个机制:cookie,则可以达到真正全 ...

  6. 微信录音文件上传到服务器以及amr转化成MP3格式

    微信公众号音频接口开发 根据业务需求,我们可能需要将微信录音保存到服务器,而通过微信上传语音接口上传到微信服务器的语音文件的有效期只有3天,所以需要将文件下载到我们自己的服务器. 上传语音接口 wx. ...

  7. 微信小程序开发心得--动画机制

    微信小程序也已出来有一段时间了,最近写了几款微信小程序项目,今天来说说感受.首先开发一款微信小程序,最主要的就是针对于公司来运营的,因为,在申请appid(微信小程序ID号)时候,需要填写相关的公司认 ...

  8. 自制裸眼3D图【推荐】

    Welcome to the World of Hidden 3D Stereograms.欢迎进入隐身3D图的世界! 网址:http://hidden-3d.com 裸眼立体图是什么? 立体图是立体 ...

  9. 201521123002《Java程序设计》第8周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 本次作业题集集合 1.List中指定元素的删除(题目4-1) 1.1 实验总结 1.提交函数实 ...

  10. 201521123028 《Java程序设计》第5周学习总结

    1. 本周学习总结 2. 书面作业 Q1.代码阅读:Child压缩包内源代码 1.1 com.parent包中Child.java文件能否编译通过?哪句会出现错误?试改正该错误.并分析输出结果. Ch ...