您好,我正在使用 IoC 容器,我想在构造函数中初始化一个服务(其中一部分涉及与数据库对话的“繁重工作”)。
此特定服务存储由注入(inject)的 IPluginToServiceProviderBridge
服务找到的信息,此信息通过 UnitOfWork
保存在数据库中。
一旦一切都启动了,带有命令的 Controller 和带有处理程序的服务将用于所有其他交互。所有命令都包含在生命周期范围内,因此 UnitOfWork
的保存和处理由处理程序而不是服务完成(这对于干净的代码非常有用)。
保存和事务的相同整洁和关注点分离不适用于服务中的 Initializer
,因为一切都发生在构造函数中:
public PluginManagerService(
IPluginToServiceProviderBridge serviceProvider,
IUnitOfWork unitOfWork)
{
this.unitOfWork = unitOfWork;
this.serviceProvider = serviceProvider;
lock (threadLock)
{
if (initialised == false)
{
LinkPluginsWithDatabase();
initialised = true;
}
// I don't like this next line, but
// not sure what else to do
this.UnitOfWork.Save();
}
}
protected void LinkPluginsWithDatabase()
{
var plugins =
this.serviceProvider.GetAllPlugins();
foreach (var plugin in plugins)
{
var db = new PluginRecord
{
interfaceType = plugin.InterfaceType;
var id = plugin.Id;
var version = plugin.Version;
}
// store in db via unit of work repository
this.unitOfWork.PluginsRepository.Add(db);
}
}
几点:
理想情况下,我想避免使用工厂,因为它会使范围生命周期的处理复杂化,如果我知道如何重构,我会很乐意重构以更好地分离。
我真的想避免为服务使用单独的 Init()
方法,虽然它允许通过命令/处理程序进行事务和保存,但需要大量检查代码,我相信这一点还会引入时间问题。
鉴于上述情况,在我的构造函数中调用 UnitOfWork.Save()
是否可以接受,或者我是否可以重构以获得更清晰的代码和更好的分离?
最佳答案
让服务的构造函数做的不仅仅是将其依赖项存储在私有(private)字段中,这在应用依赖项注入(inject)时被认为是不好的做法,因为这会导致对象图的构造失败,减慢图的构建速度,并使单元测试复杂化。
我从您的问题中了解到,您需要在应用程序启动时进行一些初始化。这很好,因为有一些初始化阶段是很正常的,但不要在构造函数中这样做。只需将此初始化移动到应用程序启动代码的末尾,在您配置容器之后(以及在您选择验证您的配置之后)。
我想象你的代码看起来像这样:
public void Application_Start(object s, EventArgs e)
{
Container container = new Container();
Bootstrap(container);
InitializeApplication(container);
}
private void InitializeApplication(
Container container)
{
using (this.container.BeginLifetimeScope())
{
var pluginManager = this.container
.GetInstance<PluginManagerService>();
pluginManager.LinkPluginsWithDatabase();
var unitOfWork =
container.GetInstance<IUnitOfWork>();
unitOfWork.Save();
}
}
您甚至可以为您的 PluginManagerService
编写装饰器,但这可能有点过度设计,但是......它可能看起来像这样:
public class InitializingPluginManagerServiceDecorator
: IPluginManagerService
{
private static readonly object syncRoot = new object();
private static bool initialized;
private IPluginManagerService decorated;
private Container container;
public InitializingPluginManagerServiceDecorator(
IPluginManagerService decorated,
Container container,
IPluginToServiceProviderBridge serviceProvider)
{
this.pluginManagerService = pluginManagerService;
this.container = container;
this.serviceProvider = serviceProvider;
}
public void PluginManagerServiceMethod()
{
this.InitializeInLock();
this.decorated.PluginManagerServiceMethod();
}
private void InitializeInLock()
{
if (!initialized)
{
lock (syncRoot)
{
if (!initialized)
{
this.InitializeInScope();
}
}
initialized = true;
}
}
private void InitializeInScope()
{
using (this.container.BeginLifetimeScope())
{
this.InitializeWithSave();
}
}
private void InitializeWithSave()
{
var uow =
this.container.GetInstance<IUnitOfWork>()
var initializer = this.container
.GetInstance<PluginManagerServiceInitializer>();
initializer.Initialize();
uow.Save();
}
}
这个装饰器可以包裹一个IPluginManagerService
,确保系统在我们第一次使用IPluginManagerService
之前被初始化,并确保它只被初始化就一次。实际的初始化逻辑被移动到一个单独的类(SRP),装饰器依赖于它:
public class PluginManagerServiceInitializer
{
private IUnitOfWork unitOfWork;
private IPluginToServiceProviderBridge serviceProvider;
public PluginManagerServiceInitializer(
IUnitOfWork unitOfWork,
IPluginToServiceProviderBridge serviceProvider)
{
this.unitOfWork = unitOfWork;
this.serviceProvider = serviceProvider;
}
public void Initialize()
{
var plugins =
from plugin in this.serviceProvider.GetAllPlugins()
select new PluginRecord
{
interfaceType = plugin.InterfaceType;
var id = plugin.Id;
var version = plugin.Version;
};
unitOfWork.PluginsRepository.AddRange(plugins);
}
}
关于c# - IoC 在构造函数中使用繁重的工作来初始化服务,但避免使用临时的 Init() 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11149394/