4

拡張可能なアプリケーションをサポートするために、ASP.NET MVC 4 で MEF 2 を使用しようとしています。この質問には実際には 2 つの部分があります (問題ないことを願っています)。

  1. Microsoft.Composition と MVC コンテナー コード ( MEF/MVC デモ ソース) を使用してICoreService、、、、およびの DI として Ninject を置き換えるにはどうすればよいでしょうか? ICoreRepositoryIUnitOfWorkIDbContext
    Ninject と MVC コンテナーの両方を同時に使用することはできないようです (多くの人が「当たり前」と言っていると思います) ので、可能であれば MEF を使用したいと考えています。[Export]Web プロジェクトに加えて 2 つのアセンブリにまたがって、Ninject を削除し、関連する実装のそれぞれに属性を設定しようとしましSave()たが、エラーなしで持続できませんでした。私はそれをシングルトンの問題と解釈しましたが、それを整理する方法を理解できませんでした(含む[Shared])。

  2. 実行時に複数のアセンブリを動的にロードするにはどうすればよいですか? 特定の DLL をロード
    するために使用する方法は理解していますが、アプリケーションを適切に拡張可能にするためには、Microsoft.Composition パッケージ (CompositionContainer.AddAssemblies()おもう?); IPluggable独自の UI、サービス、およびリポジトリ レイヤーを含み、コア サービス/リポジトリにも結び付けられるすべての (または何でも) アセンブリをロードできるようにします。

EDIT 1
もう少し読むと、実際にはシングルトンの問題である最初の問題が解決されました。CoreDbContext にアタッチ[Shared(Boundaries.HttpRequest)]すると、永続性の問題が解決されました。単純に試してみると[Shared]、「シングルトン化」がアプリケーション レベル (クロス リクエスト) に拡張され、編集されたオブジェクトが既に EF キャッシュにあるという例外がスローされました。

EDIT 2
以下の Nick Blumhardt の回答から「肉」をロードする反復アセンブリを使用して、Global.asax.cs コードを更新しました。彼のコードの標準の MEF 2 コンテナーは、おそらく MEF 2(?) MVC コンテナーを使用しているため、私のコードでは機能しませんでした。概要: 以下にリストされているコードは、希望どおりに機能するようになりました。


CoreDbContext.cs (Data.csproj)

[Export(typeof(IDbContext))]
[Shared(Boundaries.HttpRequest)]
public class CoreDbContext : IDbContext { ... }

CoreRepository.cs (Data.csproj)

[Export(typeof(IUnitOfWork))]
[Export(typeof(ICoreRepository))]
public class CoreRepository : ICoreRepository, IUnitOfWork
{
    [ImportingConstructor]
    public CoreRepository(IInsightDbContext context)
    {
        _context = context;
    }

    ... 
}

CoreService.cs (Services.csproj)

[Export(typeof(ICoreService))]
public class CoreService : ICoreService
{
    [ImportingConstructor]
    public CoreService(ICoreRepository repository, IUnitOfWork unitOfWork)
    {
        _repository = repository;
        _unitOfWork = unitOfWork;
    }

    ... 
}

UserController.cs (Web.csproj)

public class UsersController : Controller
{
    [ImportingConstructor]
    public UsersController(ICoreService service)
    {
        _service = service;
    }

    ... 
}

Global.asax.cs (Web.csproj)

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        CompositionProvider.AddAssemblies(
            typeof(ICoreRepository).Assembly,
            typeof(ICoreService).Assembly,
        );

        // EDIT 2 -- 
        // updated code to answer my 2nd question based on Nick Blumhardt's answer
        foreach (var file in System.IO.Directory.GetFiles(Server.MapPath("Plugins"), "*.dll"))
        {
            try
            {
                var name = System.Reflection.AssemblyName.GetAssemblyName(file);
                var assembly = System.Reflection.Assembly.Load(name);
                CompositionProvider.AddAssembly(assembly);
            }
            catch
            {
                // You'll need to craft exception handling to
                // your specific scenario.
            }
        }
    }
}
4

3 に答える 3

1

MEF でできることとできないことについて、少し誤解があると思います。

当初、MEF は純粋な拡張性アーキテクチャとして考えられていましたが、フレームワークが最初のリリースまで進化するにつれて、DI コンテナーとしても完全にサポートされるようになりました。MEF は依存関係の挿入を処理し、そのExportProviderアーキテクチャを通じて処理します。MEF で他の DI フレームワークを使用することも完全に可能です。したがって、実際には、物事を達成する方法はいくつかあります。

  1. MEF にプラグインできる をビルドしNinjectExportProviderて、MEF が利用可能なエクスポートを検索するときに、Ninject コンテナーに問い合わせることができるようにします。
  2. Common Services Locator パターンの実装を使用して、MEF と Ninject の間、またはその逆をブリッジします。

拡張性のために MEF を使用しているため、おそらく前者を使用することをお勧めします。これにより、Ninject コンポーネントが MEF に公開され、さらに MEF がそれらをプラグインに公開するからです。

もう 1 つ考慮すべき点は、少し残念なことですが、実際には、ASP.NET 上の Wordpressなどの機能を自動的にプラグインする余地があまりないことです。ASP.NET はコンパイルおよび管理された環境であるため、実行時に手動でアセンブリをロードして遅延バインディングを行うか、アプリケーションを再起動して新しいプラグインを取得する必要があります。アプリケーションを介して新しい拡張機能をプラグインします。

私のアドバイスは、起動時に拡張ポイントを選択するようにアーキテクチャを計画し、コアの変更には展開とアプリケーションの再起動が必要であると想定することです。

尋ねられた直接の質問に関して:

  1. プロバイダーによって使用されるを作成するために内部的に使用されるインスタンスのCompositionProvider受け入れ。したがって、コンテナをインスタンス化する方法をカスタマイズするポイントとしてこれを使用できます。は次の方法をサポートしています。ContainerConfigurationCompositionContainerContainerConfigurationWithProvider

    var configuration = new ContainerConfiguration().WithProvider(new NinjectExportDescriptorProvider(kernel)); CompositionProvider.SetConfiguration(configuration);

どこNinjectExportDescriptorProviderにある可能性があります:

public class NinjectExportDescriptorProvider: ExportDescriptorProvider
{
  private readonly IKernel _kernel;

  public NinjectExportDescriptorProvider(IKernel kernel)
  {
    if (kernel == null) throw new ArgumentNullException("kernel");

    _kernel = kernel;
  }

  public override IEnumerable<ExportDescriptorPromise> GetExportDescriptors(
    CompositionContract contract, DependencyAccessor dependencyAccessor)
  {
    var type = contract.ContractType;

    if (!_kernel.GetBindings(type).Any())
      return NoExportDescriptors;

    return new[] {
      new ExportDescriptorPromise(
        contract,
        "Ninject Kernel",
        true, // Hmmm... need to consider this, setting it to true will create it as a shared part, false as new instance each time,
        NoDependencies,
        _ => ExportDescriptor.Create((c, o) => _kernel.Get(type), NoMetadata)) };
    }      
  }
}

AppSettingsExportDescriptorProvider注: 私はこれをテストしていません。これはすべて理論であり、http : //mef.codeplex.com/wikipage?title=ProgrammingModelExtensionsの例に基づいています。

の使用は軽量な構成を中心に構築されExportProviderているため、標準の の使用とは異なります。CompostionProviderしかし、本質的には、Ninject カーネルへのアクセスをまとめて、CompositionContainer.

  1. 特定の新しいプロバイダー (上記を参照) を追加する場合と同様に、 を使用しContainerConfigurationて、おそらく次のような利用可能なアセンブリを読み取ることができます。

    var configuration = new ContainerConfiguration().WithAssemblies(AppDomain.GetAssemblies())

繰り返しますが、これをすべてテストしたわけではありませんが、少なくとも正しい方向に向けられることを願っています.

于 2012-08-07T10:01:20.193 に答える
1

私の理解が正しければ、ディレクトリからすべてのアセンブリを読み込んでコンテナに読み込むコードを探していると思います。これを行うためのスケルトンは次のとおりです。

var config = new ContainerConfiguration();
foreach (var file in Directory.GetFiles(@".\Plugins", "*.dll"))
{
   try
   {
       var name = AssemblyName.GetAssemblyName(file);
       var assembly = Assembly.Load(name);
       config.WithAssembly(assembly);
   }
   catch
   {
       // You'll need to craft exception handling to
       // your specific scenario.
   }
}
var container = config.CreateContainer();
// ...

Hammett はこのシナリオについて説明し、F# のより完全なバージョンをここで示しています: http://hammett.castleproject.org/index.php/2011/12/a-decent-directorycatalog-implementation/

これは、アプリケーションの起動後にディレクトリに追加されたアセンブリを検出しないことに注意してください - Microsoft.Composition はそのような使用を意図していません。ユーザーにアプリの再起動を促します。チッ!

于 2012-08-14T04:00:36.953 に答える
1

MEF は、DI フレームワークとして使用するためのものではありません。つまり、「プラグイン」(それが何であれ) の構成をインフラストラクチャの依存関係から分離し、前者を MEF 経由で、後者を任意の DI フレームワーク経由で実装する必要があります。

于 2012-08-06T20:36:41.207 に答える