IoC初始化服务在构造函数中繁重但避免使用时态Init()方法

g18*_*18c 6 .net c# design-patterns dependency-injection ioc-container

嗨我正在使用IoC容器,我想在构造函数中初始化一个服务(其中一部分涉及'繁重的工作'与数据库交谈).

此特定服务存储由注入IPluginToServiceProviderBridge服务找到的信息,此信息通过a保存在数据库中UnitOfWork.

一旦所有内容都启动,带有命令的控制器和带有处理程序的服务将用于所有其他交互.所有命令都包含在生命周期范围内,因此保存和处理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);
    }
}
Run Code Online (Sandbox Code Playgroud)

几点:

理想情况下,我想避免使用工厂,因为它使范围生命周期的处理复杂化,如果我知道如何,我很乐意重构更好的分离.

我真的想避免Init()为服务提供单独的方法,同时它允许通过命令/处理程序进行事务和保存,需要大量的检查代码,我相信这也会引入时间问题.

鉴于上述情况,UnitOfWork.Save()在我的构造函数中调用是否可以接受,还是可以重构更清晰的代码和更好的分离?

Ste*_*ven 5

在应用依赖项注入时,让服务的构造函数不仅仅将其依赖项存储在私有字段中被认为是不好的做法,因为这样可以使对象图的构造失败,减慢图形的构建速度,并使单元测试复杂化.

我从你的问题中读到的是你需要在应用程序启动时进行一些初始化.这很好,因为有一些初始化阶段是很正常的,但是不要在构造函数中执行此操作.在配置容器之后(以及在您选择验证配置之后),只需将此初始化移动到应用程序启动代码的末尾.

我想你的代码看起来像这样:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

你甚至可以为你写一个装饰器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();    
    }
}
Run Code Online (Sandbox Code Playgroud)

这个装饰器可以包裹在一个IPluginManagerService,并确保系统在IPluginManagerService我们第一次使用之前初始化,并确保它只初始化一次.实际的初始化逻辑被移动到单独的类(SRP),装饰器依赖于该类(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);
    }
}
Run Code Online (Sandbox Code Playgroud)