EntityFramework Core依赖注入上下文方式不同造成内存泄漏了解一下?
前言
这个问题从未遇见过,是一位前辈问我EF Core内存泄漏问题时我才去深入探讨这个问题,刚开始我比较惊讶,居然还有这种问题,然后就有了本文,直接拿前辈的示例代码并稍加修改成就了此文,希望对在自学EF Core过程中的童鞋能有些许帮助。
EntityFramework Core内存泄漏回顾
接下来我将用简单示例代码来还原整个造成EntityFramework Core内存泄漏的过程,同时在这个过程中您也可思考一下其中的原因和最终的结果是否一致。
public class TestA
{
public long Id { get; set; }
public string Name { get; set; }
}
public class EFCoreDbContext : DbContext
{
public EFCoreDbContext(DbContextOptions options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("data source=WANGPENG;User Id=sa;Pwd=sa123;initial catalog=MemoryLeak;integrated security=True;MultipleActiveResultSets=True;");
base.OnConfiguring(optionsBuilder);
} public DbSet<TestA> TestA { get; set; }
}
public class TestUserCase
{
public void InvokeMethod(IServiceProvider serviceProvider)
{
EFCoreDbContext _context = null; if (_context == null)
{
_context = serviceProvider.GetRequiredService<EFCoreDbContext>();
} for (var i = ; i < ; i++)
{
var testA = _context.TestA.FirstOrDefault();
Console.WriteLine(i);
}
}
}
如上是整个示例代码,重头戏来了,接下来我们在控制台中来通过依赖注入上下文,并获取注入容器中的上下文并调用上述TestUserCase类中的方法,如下:
var services = new ServiceCollection();
services.AddDbContext<EFCoreDbContext>(options => options.UseSqlServer("data source=WANGPENG;User Id=sa;Pwd=sa123;initial catalog=MemoryLeak;integrated security=True;MultipleActiveResultSets=True;"));
var serviceProvider = services.BuildServiceProvider();
for (int i = ; i < ; i++)
{
var test = new TestUserCase();
test.InvokeMethod(serviceProvider);
}

通过上述测试内存基本在15兆左右,当然根据机器配置不同最终得到的结果有所差异,但是内存基本没有什么大的波动,接下来我们来改造上述代码。上述我们将serviceProvider通过方法传递到TestUserCase中的InvokeMethod方法中,为了简便我们将获取到的serviceProvider改造成静态的,如下:
public static class ServiceLocator
{
private static IServiceCollection _services; public static IServiceProvider Instance
{
get
{
if (_services == null)
return null;
else
return _services.BuildServiceProvider();
}
} public static void Init(IServiceCollection services)
{
_services = services;
}
}
public class TestUserCase
{
public void InvokeMethod()
{
IServiceScope _serviceScope = null;
EFCoreDbContext _context = null;
if (_context == null)
{
_serviceScope = ServiceLocator.Instance.GetRequiredService<IServiceScopeFactory>().CreateScope();
_context = _serviceScope.ServiceProvider.GetRequiredService<EFCoreDbContext>();
} for (var i = ; i < ; i++)
{
var testA = _context.TestA.FirstOrDefault();
Console.WriteLine(i);
}
}
}
var services = new ServiceCollection();
services.AddDbContext<EFCoreDbContext>(options => options.UseSqlServer("data source=WANGPENG;User Id=sa;Pwd=sa123;initial catalog=MemoryLeak;integrated security=True;MultipleActiveResultSets=True;"));
ServiceLocator.Init(services);
for (int i = ; i < ; i++)
{
var test = new TestUserCase();
test.InvokeMethod();
}
如上我们通过ServiceLocator类来构建serviceProvider,并将返回serviceProvider赋值给静态变量,然后在我们调用的方法中直接获取容器中的上下文,这样就免去了传递的麻烦。

经过我们上述改造后最终运行内存达到了比较可怕的三百多兆,看来上下文压根就没进行GC,那我是不是改造成如下就可以了呢?
var scopeFactory = ServiceLocator.Instance.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
using (var context = scope.ServiceProvider.GetRequiredService<EFCoreDbContext>())
{
for (var i = ; i < ; i++)
{
var testA = context.TestA.FirstOrDefault();
Console.WriteLine(i);
}
}

原以为是上下文没有及时得到释放而导致内存激增,但是看到上述结果依然一样没有任何改变,问题是不是到此就结束了呢?下面我们改变注入上下文的方式看看,如下:
var services = new ServiceCollection();
var options = new DbContextOptionsBuilder<EFCoreDbContext>()
.UseSqlServer("data source=WANGPENG;User Id=sa;Pwd=sa123;initial catalog=MemoryLeak;integrated security=True;MultipleActiveResultSets=True;")
.Options;
services.AddScoped(s => new EFCoreDbContext(options));

当我们改变了注入上下文方式后发现此时不会造成内存泄漏,也就是说上下文得到了GC,无论是我是否是手动释放上下文即通过Using包括或者不包括都不会出现内存泄漏问题。通过注入方式不同得到的结果截然不同,但是在我们的理解中通过AddDbContext注入上下文中的第二个参数是默认为Scope,那和我们通过AddScoped注入上下文应该是一样对不对,那为何结果又不同呢?岂不是冲突了吗?在Web不会出现这样的问题,未深入研究,我猜测其原因可能如下:
通过AddDbContext注入上下文只适用于Web应用程序即只对Web应用程序有效而对控制台程序可能无效,同时在Web应用程序中AddDbContext注入上下文和AddScoped注入上下文一致,而对于控制台程序存在不一致问题。一言以蔽之,在Web和Console中通过AddDbContext注入上下文可能存在处理机制不同。
总结
不知如上浅薄分析是否有漏洞或者说代码有错误的地方,期待看到本文的您能有更深入的见解可留下您的评论,如果结果是这样不建议在控制台中使用AddDbContext方法注入上下文。
EntityFramework Core依赖注入上下文方式不同造成内存泄漏了解一下?的更多相关文章
- Webservice WCF WebApi 前端数据可视化 前端数据可视化 C# asp.net PhoneGap html5 C# Where 网站分布式开发简介 EntityFramework Core依赖注入上下文方式不同造成内存泄漏了解一下? SQL Server之深入理解STUFF 你必须知道的EntityFramework 6.x和EntityFramework Cor
Webservice WCF WebApi 注明:改编加组合 在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下, ...
- .net core options 依赖注入的方式
options 依赖注入的方式 public class JwtSettingsOptions { public const string JwtSettings = "JwtSetting ...
- [译]ASP.NET Core依赖注入深入讨论
原文链接:ASP.NET Core Dependency Injection Deep Dive - Joonas W's blog 这篇文章我们来深入探讨ASP.NET Core.MVC Core中 ...
- ASP.NET Core依赖注入——依赖注入最佳实践
在这篇文章中,我们将深入研究.NET Core和ASP.NET Core MVC中的依赖注入,将介绍几乎所有可能的选项,依赖注入是ASP.Net Core的核心,我将分享在ASP.Net Core应用 ...
- Spring(八):Spring配置Bean(一)BeanFactory&ApplicationContext概述、依赖注入的方式、注入属性值细节
在Spring的IOC容器里配置Bean 配置Bean形式:基于xml文件方式.基于注解的方式 在xml文件中通过bean节点配置bean: <?xml version="1.0&qu ...
- 在WPF中使用依赖注入的方式创建视图
在WPF中使用依赖注入的方式创建视图 0x00 问题的产生 互联网时代桌面开发真是越来越少了,很多应用都转到了浏览器端和移动智能终端,相应的软件开发上的新技术应用到桌面开发的文章也很少.我之前主要做W ...
- # ASP.NET Core依赖注入解读&使用Autofac替代实现
标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Aut ...
- Spring学习(三)——Spring中的依赖注入的方式
[前面的话] Spring对我太重要了,做个关于web相关的项目都要使用Spring,每次去看Spring相关的知识,总是感觉一知半解,没有很好的系统去学习一下,现在抽点时间学习一下Spring.不知 ...
- net core 依赖注入问题
net core 依赖注入问题 最近.net core可以跨平台了,这是一个伟大的事情,为了可以赶上两年以后的跨平台部署大潮,我也加入到了学习之列.今天研究的是依赖注入,但是我发现一个问题,困扰我很久 ...
随机推荐
- Angular路由——路由守卫
一.路由守卫 当用户满足一定条件才被允许进入或者离开一个路由. 路由守卫场景: 只有当用户登录并拥有某些权限的时候才能进入某些路由. 一个由多个表单组成的向导,例如注册流程,用户只有在当前路由的组件中 ...
- EF Linq中的左连接Left Join查询
linq中的join是inner join内连接,就是当两个表中有一个表对应的数据没有的时候那个关联就不成立. 比如表A B的数据如下 from a in A join b in B on a.BId ...
- 学习了解 Exchanger - 实现生产者消费者模型
例子很简单 Exchanger可以理解为消息队列或者说是一个通信管道,从一边拿到消息,另外一边进行消费. 不过这个是同步实现的,消费者在exchange之前,生产者一直处于等待状态,而不是一直生产. ...
- Django+xadmin打造在线教育平台(九)
目录 在线教育平台(一) 在线教育平台(二) 在线教育平台(三) 在线教育平台(四) 在线教育平台(五) 在线教育平台(六) 在线教育平台(七) 在线教育平台( ...
- django优化和扩展(一)
mysql优化基础 进行django产品开发或上线之前,有必要了解一下mysql的基础知识,orm太过抽象,导致很多朋友对于mysql了解得太少,而且orm不像sqlalchemy那样可以跟mysql ...
- Hello Django
首先安装Django: 1.cmd界面,输入"pip3 install django" 2.输入"django-admin help",如下图表示安装成功 ...
- JavaScript 字符串反转
第一种方法:使用内置方法 split()方法将一个字符串对象的每个字符拆出来,并且将每个字符串当成数组的每个元素 reverse()方法用来改变数组,将数组中的元素倒个序排列,第一个数组元素成为最后一 ...
- 如何查看chrome浏览器已保存的密码
该方法是针对在chrome中已经存储了登陆密码的情况. chrome版本是 66.0.3359.139(正式版本) (64 位),不知道哪天会改了这个bug. 一般来说,我们登陆chrome浏览器已经 ...
- mint-ui之picker爬坑记
picker的数据来源为动态获取时,数据无法正常渲染!因为方法不对,所以坑大了!深刻地体会到'业不精,我之过',谨以此文,深刻地记录一下踩坑及爬坑的整个过程,以便日后不再入坑,也给后来者提供一下参考 ...
- @EnableTransactionManagement注解理解
@EnableTransactionManagement表示开启事务支持,在springboot项目中一般配置在启动类上,效果等同于xml配置的<tx:annotation-driven /&g ...