基于上篇文章《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. 以打印日志为荣之logging模块详细使用

    啄木鸟社区里的Pythonic八荣八耻有一条: 以打印日志为荣 , 以单步跟踪为耻; 很多程序都有记录日志的需求,并且日志中包含的信息既有正常的程序访问日志,还可能有错误.警告等信息输出,python ...

  2. Javaweb项目碰到的问题- Access denied for user 'root'@'localhost' (using password: YES)

    出现未给localhost root用户授权,主要是项目中存在的多个xxx.properties,其中用户名为root的password的值不完全相同导致的,使用eclipse的search 功能找到 ...

  3. Java基础---IO(三)--IO包中的其他类

    第一讲     对象序列化 一.概述 将堆内存中的对象存入硬盘,保留对象中的数据,称之为对象的持久化(或序列化).使用到的两个类:ObjectInputStream和ObjectOutputStrea ...

  4. Centos7.2部署.Net Core2.0 WebApi

    部署前准备 1.VisualStudio2017+.netcore2.0SDK 2.Centos7.2 3.SecureCRT,Xftp(根据自己喜好) 创建WebApi项目 修改Program.cs ...

  5. Maven项目下update maven后Eclipse报错:java.lang.ClassNotFoundException: ContextLoaderL

    Maven项目下update maven后Eclipse报错:java.lang.ClassNotFoundException: ContextLoaderL     严重: Error config ...

  6. 三分钟读懂TT猫分布式、微服务和集群之路

    针对入门新手的普及,有过大型网站技术架构牛人路过,别耽误浪费了时间,阅读之前,请确保有一定的网络基础,熟练使用Linux,浏览大概需要3-5分钟的时间,结尾有彩蛋. 目录 分布式 微服务 负载均衡集群 ...

  7. 慕课网视频破解付费分享-前端开发-Python等

    微信小程序 慕课网   BAT大牛经验总结全面深入解读Android面试   前端JS基础面试技巧   vue2.0+node.js+mongodb全栈打造商城   Vue.js高级实战-开发移动端音 ...

  8. CSS背景效果

    前面的话 本文将详细介绍CSS背景效果 条纹背景 [双条纹背景] background:linear-gradient(#fb3 50%, #58a 50%); background-size: 10 ...

  9. java常见面试题(二)

    1.java集合类 Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements).JavaSDK不提供直接继承自Collect ...

  10. Android学习记录:获取联系人

    在AndroidManifest中申请读取联系人的权限 <uses-permission android:name = "android.permission.READ_CONTACT ...