c# - IoC 在构造函数中使用繁重的工作来初始化服务,但避免使用临时的 Init() 方法

标签 c# .net design-patterns dependency-injection ioc-container

您好,我正在使用 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/

相关文章:

c# - 尽管 intellisense 列出了定义,但找不到定义?

.net - 如何阻止 Json.NET JArray.Parse 更改 UTC 日期?

c# - Sitecore 7 搜索所有内容

javascript - 如何组合匿名函数?

c++ - 是否有一个有效的案例来创建一个立即销毁且不直接在 C++ 中使用的临时对象?

c# - 参数的可空性与约束类型不匹配

c# - int rosu=Color.red.getRGB() 从 Java 到 C#

c# - 将一个方法作为参数传递给另一个方法

c# - 不允许调整窗口窗体的大小

java - 如何在不将对象作为字段包含在内的情况下使项目中的所有类都可以使用该对象?