基于上篇文章《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. cmder修改默认打开路径

    win + alt + p //打开设置 选择Startup-Task,修改{cmd::Cmder}项,把: *cmd /k "%ConEmuDir%\..\init.bat" - ...

  2. Java基础---多态、内部类、异常、包

    第一讲     多态 多态可以理解为事物存在的多种体现形态. 例:动物中猫,狗.猫这个对象对应的类型是猫类型,如:猫 x = new猫(); 同时猫也是动物中的一种,也可以把猫称为动物.动物  y = ...

  3. 将git commit的默认编辑器从nano转为vim

    修改系统的配置 git config --global core.editor vim 针对 git 项目修改 .git/config core 中添加 editor=vim

  4. BootKit病毒——“异鬼Ⅱ”的前世今生

    七月底,一种名为"异鬼Ⅱ"的木马在全网大肆传播.一个多月过去了,风声渐渐平息,之前本来准备专门就这个木马写一篇博客的,结果拖到现在,幸好时间隔得还不算太久.闲话不多说,回到正题. ...

  5. HTTP请求范例

    package com.grefr.basemethod; /*JAVA发送HTTP请求,返回HTTP响应内容,实例及应用 博客分类: JAVA实现 Java.netBeanJDKApache . J ...

  6. Windows系统安装Azure CLI

    本文将介绍在Windos系统下如下安装CLI 1.打开Azure官方链接:https://www.azure.cn/downloads/ 2.按照向导进行安装 3.打开Windows Powershe ...

  7. POJ 3463 最(次)短路条数

    Sightseeing Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 9497   Accepted: 3340 Descr ...

  8. grunt之watch续

    上一回没有说完,我就是这样,做之前心中波澜壮阔,锦绣山河,等画完小草开始烦躁,完成鲜花出现动摇,然后心神涣散,最后虎头蛇尾. 现在弥补下之前遗漏的问题. watch(V0.6.1)的tasks和opt ...

  9. CCIE-MPLS VPN-实验手册(下卷)

    10:跨域的MPLS VPN (Option A) 10.1 实验拓扑 10.1 实验需求 a.       R1 R2 R3 组成P-NETWORK R1 R2 R3 位于AS 1,底层协议采用EI ...

  10. 转:H2 入门

    H2 Database做为轻量级的内嵌数据库,功能十分强大,而且运行时只需要一个jar包即可,下表是官网的描述: 更详细的对比见官网页面: http://www.h2database.com/html ...