我需要对使用singleton作用域的DI容器管理的对象使用synclock吗
本文关键字:管理 synclock 对象 作用域 singleton DI | 更新日期: 2023-09-27 18:21:36
我有以下代码:
public class DotLessFactory
{
private LessEngine lessEngine;
public virtual ILessEngine GetEngine()
{
return lessEngine ?? (lessEngine = CreateEngine());
}
private ILessEngine CreateEngine()
{
var configuration = new LessConfiguration();
return new LessFactory().CreateEngine(configuration);
}
}
让我们假设如下:
- 我总是希望返回相同的ILessEngine实例
- 我使用DI容器(本例中我将使用StructureMap)来管理我的对象
- 由于第1个假设,我的对象的寿命将是辛格尔顿
- CreateEngine内部的代码执行通过NuGet包引用的代码
- DotLess只是一个例子。此代码可适用于任何类似的NuGet包
方法1
我使用在DI容器中注册我的对象
For<DotLessFactory>().Singleton().Use<DotLessFactory>();
For<ILessEngine>().Singleton().Use(container => container.GetInstance<DotLessFactory>().GetEngine());
For<ISomeClass>().Singleton().Use<SomeClass>();
现在,我可以将ILessEngine添加到构造函数中,并让容器按照下面的代码注入它的实例。
public class SomeClass : ISomeClass
{
private ILessEngine lessEngine;
public SomeClass(ILessEngine lessEngine)
{
this.lessEngine = lessEngine;
}
}
方法2
引入一个IDotLessFactory接口,该接口公开了GetEngine方法。我使用在DI容器中注册我的对象
For<IDotLessFactory>().Singleton().Use<DotLessFactory>();
For<ISomeClass>().Singleton().Use<SomeClass>();
现在,我的工厂将按照下面的代码创建一个ILessEngine的实例。
public class SomeClass : ISomeClass
{
private ILessEngine lessEngine;
public SomeClass(IDotLessFactory factory)
{
Factory = factory;
}
public IDotLessFactory Factory { get; private set; }
public ILessEngine LessEngine
{
get
{
return lessEngine ?? (lessEngine = Factory.GetEngine());
}
}
}
我的问题是:
- 在ILessEngine方面,方法1和方法2的根本区别是什么?在方法1中,ILessEngine由容器管理,而在方法2中则不是。每种方法的优点/缺点是什么?一种方法比另一种好吗
- 我是否需要在CreateEngine方法中使用同步锁来确保任何方法的线程安全?在这种情况下,我什么时候应该/不应该使用synclock
- 我看到过在CreateEngine方法中使用Activator.CreateInstance的例子,而不是直接新建对象。是否有理由使用这种方法?这与没有在工厂对象中引入NuGet包内对象的直接依赖关系有关吗
- 让我们假设引用的NuGet包在后台与HttpContext一起工作。在singleton作用域中注册我的工厂会对HttpContext产生任何负面影响吗?或者这无关紧要,因为我认为NuGet包很可能管理HttpContext本身的作用域
- 最后,DotLessFactory最终将与Bundles(Microsoft.AspNet.Web.Optimization-NuGet包)一起使用,并且Bundle仅在ApplicationStart上实例化(不由容器管理)。Bundle将依赖于DotLessFactory的注入实例。这一事实与上述问题有什么不同吗
任何反馈都将非常有帮助。
具体回答这些问题并不简单,但请允许我提供一些非详尽的评论:
-
据我所知,没有一种方法可以保证需求#1(singleton)。这是因为两个线程可以同时执行查找,并且都将lessEngine求值为null并触发创建新实例。如果StructureMap查找是线程安全的,那么第一种方法最终可能是线程安全,但如果是这样的话,我会感到惊讶(无论如何,你不希望你的代码依赖于第三方库中的实现"细节")。
-
两种解决方案都犯了相同的错误,本质上是在不保护整个代码区域的情况下检查是否已经创建了实例。为了解决这个问题,引入一个私有对象变量来锁定,并保护创建实例的代码区域:
private object engineLock = new object(); public virtual ILessEngine GetEngine() { lock( engineLock ) { return lessEngine ?? (lessEngine = CreateEngine()); } }
顺便说一句,如果你能让StructureMap处理整个对象链的构造,这就没有必要了,因为这将取决于StructureMap来确保根据你的容器配置的单例需求。
-
只有当您知道新对象有默认构造函数(例如,通过类型参数代码中的泛型约束)或您有对它们的编译时引用时,才能创建新对象。由于IoC主要创建它在编译时不知道的东西,并且在执行此操作时经常需要传递参数,因此在开头使用Activator.CreateInstance。据我所知,使用"new"生成IL来调用Activator.CreateInstance,所以最终结果是一样的。
-
HttpContext的生存期是在应用程序之外管理的(通过ASP.NET),因此不存在作用域问题。HttpContext.Current要么被设置,要么不被设置,如果它没有被设置,那么你做的工作太早了,它不可用(或者在永远不可用的上下文中执行,例如在ASP.NET之外)
-
呃,不确定你在考虑什么潜在的问题,但我最好的猜测是它不会对你的代码产生任何影响。
希望这能有所帮助!