6

外部プラグインをサポートする ASP.NET MVC プロジェクトに取り組んでいます。現在、Unity から Autofac に移行しています。Autofac の有効期間オブジェクトをラップする必要があるため、Unity でプラグインがそれを参照する必要はありません。これは何かできる。

public sealed class UnityScopeFactory : IDependencyScopeFactory
{
    private HttpRequestScope _httpRequest;

    private SingletonScope _singleton;

    private TransientScope _transient;

    public IDependencyScope HttpRequest()
    {
        return _httpRequest ?? (_httpRequest = new HttpRequestScope());
    }

    public IDependencyScope Singleton()
    {
        return _singleton ?? (_singleton = new SingletonScope());
    }

    public IDependencyScope Transient()
    {
        return _transient ?? (_transient = new TransientScope());
    }

    private class HttpRequestScope : IDependencyScope
    {
        public object CreateScope()
        {
            return new HttpPerRequestLifetimeManager();
        }
    }

    private class SingletonScope : IDependencyScope
    {
        public object CreateScope()
        {
            return new ContainerControlledLifetimeManager();
        }
    }

    private class TransientScope : IDependencyScope
    {
        public object CreateScope()
        {
            return new TransientLifetimeManager();
        }
    }
}

私はAutofacで同様のことをしましたが、それが正しい方法であるかどうかはわかりません。(残念ながら)内部にあるAutofacのRegistrationBuilderを調べて、これを思いつきました。

public class AutofacScopeFactory : IDependencyScopeFactory
{
    private HttpRequestScope _httpRequest;

    private SingletonScope _singleton;

    private TransientScope _transient;

    public IDependencyScope HttpRequest()
    {
        return _httpRequest ?? (_httpRequest = new HttpRequestScope());
    }

    public IDependencyScope Singleton()
    {
        return _singleton ?? (_singleton = new SingletonScope());
    }

    public IDependencyScope Transient()
    {
        return _transient ?? (_transient = new TransientScope());
    }

    private class HttpRequestScope : IDependencyScope
    {
        public object CreateScope()
        {
            return new CurrentScopeLifetime();
        }
    }

    private class SingletonScope : IDependencyScope
    {
        public object CreateScope()
        {
            return new RootScopeLifetime();
        }
    }

    private class TransientScope : IDependencyScope
    {
        public object CreateScope()
        {
            return new CurrentScopeLifetime();
        }
    }
}

また、これを機能させた後、それを ContainerBuilder に渡すにはどうすればよいですか?

Unityでは、このようなことができました。

public sealed class UnityDependencyContainer : IDependencyContainer
{
    private readonly IUnityContainer _container;

    public UnityDependencyContainer()
    {
        _container = new UnityContainer()
    }

    public void Register<TContract, TImplementation>(IDependencyScope scope) where TImplementation : TContract
    {
        LifetimeManager manager = scope.CreateScope() as LifetimeManager;

        if (manager != null)
        {
            _container.RegisterType<TContract, TImplementation>(manager);
        }
    }
}

IComponentLifetime のインスタンスをメソッド チェーンに渡すにはどうすればよいですか? それは行き止まりですか?

public class AutofacContainer : IDependencyContainer
{
    private static readonly ContainerBuilder Builder;

    static AutofacContainer()
    {
        Builder = new ContainerBuilder();
    }

    public void RegisterType<TContract, TImplementation>(IDependencyScope scope) where TImplementation : TContract
    {
        IComponentLifetime manager = scope.CreateScope() as IComponentLifetime;

        if (manager != null)
        {
            Builder.RegisterType<TImplementation>().As<TContract>();
        }
    }
}
4

1 に答える 1

10

Autofac は、概説したようにスコープを分離しないため、四角いペグを丸い穴に合わせようとしている可能性があります

Autofac スコープはより階層的です。どのライフタイム スコープでも、子一時スコープを生成できます。たとえば、次のように表示されます...

  • コンテナ/ルートの有効期間
    • HttpRequest スコープ
      • 小さなタスク固有の一時的なスコープ

スコープを「タグ付け」し、コンポーネントを特定の名前付き/タグ付きスコープに登録できます。これが HttpRequest スコープの仕組みです。特別な識別子で「タグ付け」されます。

オブジェクトを解決するときは、オブジェクトを所有するライフタイム スコープを決定するときです。解決は、最もネストされたスコープから行われます。上記の階層では、シングルトン、リクエスト スコープなど、小さなタスク固有の一時的なスコープからアイテムを解決します。シングルトンが解決されると、ライフタイム スコープ スタックを検索し、オブジェクトの「所有権」をルート ライフタイム スコープに自動的に割り当てます。リクエストごとのアイテムが解決されると、特別な「HTTP リクエスト」識別子を使用して有効期間スコープのスタックを検索し、そこに所有権を割り当てます。ファクトリ スコープのアイテムは、現在の有効期間スコープで解決されます。

注: この説明は、その仕組みを大幅に単純化しすぎています。 Autofac サイトには、有効期間スコープのメカニズムを説明するドキュメントがあります。

要するに、上記の設計には、Autofac の処理方法と実際には「調和」していないものがいくつかあります。

DependencyScopeFactory は、独自の一時スコープまたは HttpRequest スコープを作成できません。HttpRequest スコープを開始および終了する特定の有効期間管理コンポーネントがあるため、それらを使用する必要があります。「グローバルな」一時的なスコープはないため、単に作成することはできません。

MVC を使用していると仮定すると、HttpRequest スコープは次のようになります...

public ILifetimeScope HttpRequestScope
{
  get { return AutofacDependencyResolver.Current.RequestLifetime; }
}

一時的なスコープの類似物はありません。その使用法はインラインであると想定されているためです。

using(var transientScope = parentScope.BeginLifetimeScope())
{
  // Do stuff and resolve dependencies using the transient scope.
  // The IDisposable pattern here is important so transient
  // dependencies will be properly disposed at the end of the scope.
}

コンポーネントを登録するとき、それらを「有効期間スコープに」登録しません。実際にそれらをコンポーネント レジストリに登録すると、コンポーネント登録の一部に、解決後のコンポーネントの有効期間に関する所有権情報が含まれます。

var builder = new ContainerBuilder();

// This component is factory-scoped and will be "owned" by whatever
// lifetime scope resolves it. You can resolve multiple of these
// in a single scope:
builder.RegisterType<FirstComponent>().As<ISomeInterface>();

// This component is a singleton inside any given lifetime scope,
// but if you have a hierarchy of scopes, you'll get one in each
// level of the hierarchy.
builder.RegisterType<SecondComponent>().InstancePerLifetimeScope();

// This component will be a singleton inside a specifically named
// lifetime scope. If you try to resolve it in a scope without that
// name, it'll search up the scope stack until it finds the scope
// with the right name. If no matching scope is found - exception.
builder.RegisterType<ThirdComponent>().InstancePerMatchingLifetimeScope("scopename");

// This is a per-HTTP-request component. It's just like the
// above InstancePerMatchingLifetimeScope, but it has a special
// tag that the web integration knows about.
builder.RegisterType<FourthComponent>().InstancePerHttpRequest();

コンテナ/登録に依存しないインターフェイスを作成しようとしている場合、「ライフタイム スコープ マネージャー」は必要ありません。代わりに、意図したライフタイム スコープを示すいくつかのパラメーターを渡し、適切な登録構文 (上記) を実行する必要があります。入力パラメータに基づいています。

繰り返しますが、そのドキュメントを確認することをお勧めします

また、Unity を使用している場合、Autofac には Enterprise Library Configurator パッケージがあり、Unity スタイルで Autofac を構成できます (EntLib はそれを好むため)。それはチェックすべきことかもしれません。

Unity構文をまったく使用する必要がない場合...ネイティブのAutofacの方法に移行することをお勧めします。あるコンテナを別のコンテナのように見せて動作させようとするのは、かなり骨の折れる作業です。

プラグインが個別のアセンブリなどにあると仮定すると、Autofac モジュールと一緒にいくつかの優れたアセンブリ スキャン構文を簡単に利用して、プラグインをそのように接続できます。

于 2012-12-06T16:15:23.883 に答える