6

こんにちは、私は IoC コンテナーを使用しており、コンストラクター内でサービスを初期化したいと考えています (その一部には、データベースと通信する「重い作業」が含まれます)。

この特定のサービスは、挿入されたサービスによって検出された情報を格納します。IPluginToServiceProviderBridgeこの情報は、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);
    }
}

いくつかのポイント:

スコープの有効期間の処理が複雑になるため、理想的にはファクトリの使用を避けたいと考えています。

サービスに別のメソッドを用意することは本当に避けたいと思ってInit()いますが、コマンド/ハンドラーを介したトランザクションと保存が可能になりますが、多くのチェック コードが必要になり、一時的な問題も発生すると思います。

上記を考えると、コンストラクター内で呼び出すことは受け入れられますUnitOfWork.Save()か?それとも、よりクリーンなコードとより良い分離のためにリファクタリングできますか?

4

1 に答える 1

6

依存性注入を適用する場合、サービスのコンストラクターに依存関係をプライベート フィールドに格納する以上のことをさせることは、悪い習慣と見なされます。これにより、オブジェクト グラフの構築が失敗し、グラフの構築が遅くなり、単体テストが複雑になるためです。

あなたの質問から私が読んだのは、アプリケーションの起動時に初期化を行う必要があるということです。初期化フェーズがあるのはごく普通のことなので、それで問題ありませんが、コンストラクター内でこれを行わないでください。コンテナーを構成した後 (オプションで構成を検証した後)、この初期化をアプリケーションの起動コードの最後に移動するだけです。

あなたのコードは次のようになると思います:

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();    
    }
}

このデコレータは , をラップすることができ、 us が初めて使用される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);
    }
}
于 2012-06-22T17:17:59.997 に答える