public class SingletonService { private readonly DbContext _dbContext; public SingletonService(DbContext dbContext) { _dbCOntext= dbContext; } } public class SomeController { private readonly SingletonService _singletonService; private readonly DbContext _dbContext; public SomeController(SingletonService singletonService, DbContext dbContext) { _singletOnService= singletonService; _dbCOntext= dbContext; } }
昨天晚上群里别人在讨论的问题,他们说这样注入会导致 SingletonService 里的 DbContext 释放不掉。我不是很理解,Singleton 每次运行都是同一个,SingletonService 里的 DbContext 永远只会创建一次,为什么会内存泄漏?
例子是 C#的,别的有依赖注入的语言应该也一样。
![]() | 1 crysislinux 307 天前 via Android 我觉得没问题,又不会持续增长 |
![]() | 2 timethinker 307 天前 就你贴出的这个例子而言,是的,DbContext 永远只会创建一次。 但是这种做法是有问题的,仅就这个例子而言,DbContext 不是线程安全的,这意味着当多个线程同时操作一个 DbContext 对象可能会引发数据竞争问题,破坏其内部状态的一致性。 微软官网是这样描述 DbContext 的生命周期的:The lifetime of a DbContext begins when the instance is created and ends when the instance is disposed. A DbContext instance is designed to be used for a single unit-of-work. This means that the lifetime of a DbContext instance is usually very short. 工作单元模式简单的来讲可以理解为对应于数据库的一个事务范围,在这个事务范围进行的操作会被追踪然后被提交。除非你确实有理由要一直保持一个 DbContext 的实例,并且考虑了线程安全问题,那么这么做就没什么问题。 |
3 cuso45h2o 307 天前 DbContext 是只会创建一次,但是因为它不会被dispose ,如果它的代码有问题产生了大量 tracked entities ,这一个 DbContext 也会导致内存泄露。 |
![]() | 5 ch3nz 307 天前 会“可能导致内存泄漏和行为异常”而不是“一定导致” 你的例子是属于“一定导致”。 比如你给数据库一顿操作,dbContext track 了所有数据库变化,本该释放,但是被 singleton 持有而不能释放。 |
7 cuso45h2o 307 天前 via iPhone ![]() 回复#4 如果在 Singleton 里发生的更改会累积。 #6 Microsoft.Extensions.DependencyInjection 会检测这种情况,如果把 scoped 注入到 Singleton 会报错 Cannot consume scoped service from singleton 。因为 OP 没说用的是什么 DI 接口,我假设 OP 自己实现了一个简易 DI ,这样注入会导致潜在的内存泄露的问题。 @irisdev |
![]() | 9 liuliuliuliu PRO 是的 楼上很多人都说了。 所以.net 的默认 DI 不允许这么做…… |
10 ltmst 306 天前 看到了这个例子,突然想起来知乎中一个回答说,ef 至今没有 "解决" 跨线程的 bug |
![]() | 11 ne6rd 306 天前 另外补充一点,一般不推荐在 Singleton 里用 Scoped 的类,但是非要用的话,也是有方法的。 不能直接注入,但是可以注入一个 IServiceScopeFactory, CreateScope(), 然后 scope.ServiceProvider.GetServices<>(); 这个拿到的 Scoped 实例只能在 method 级别里访问,不能把它存到实例属性上给其他 method 共享。 |