8

私は MEF を学んでおり、簡単な例 (アプリケーション) を作成して、実際にどのように機能するかを確認したいと考えていました。そこで、簡単な翻訳機を考えました。4 つのプロジェクト (DLL ファイル) を含むソリューションを作成しました。

コントラクト
Web
BingTranslator
GoogleTranslator

ContractsにはITranslateインターフェースが含まれています。名前が適用されるように、それにはコントラクト (インターフェース) のみが含まれるため、輸出者と輸入者はそれを使用できます。

public interface ITranslator
{
    string Translate(string text);
}

BingTranslatorGoogleTranslatorはどちらも、この契約の輸出者です。どちらもこのコントラクトを実装し、さまざまな翻訳サービス (Bing からのもの、Google からのもの) を提供 (エクスポート) します。

[Export(typeof(ITranslator))]
public class GoogleTranslator: ITranslator
{
    public string Translate(string text)
    {
        // Here, I would connect to Google translate and do the work.
        return "Translated by Google Translator";
    }
}

そして、次のBingTranslatorとおりです。

[Export(typeof(ITranslator))]
public class BingTranslator : ITranslator
{
    public string Translate(string text)
    {
        return "Translated by Bing";
    }
}

さて、私のWebプロジェクトでは、単純にユーザーからテキストを取得し、それらの翻訳者 (Bing と Google) のいずれかで翻訳し、結果をユーザーに返したいと考えています。したがって、私のWebアプリケーションでは、翻訳者に依存しています。したがって、この方法でコントローラーを作成しました。

public class GeneralController : Controller
{
    [Import]
    public ITranslator Translator { get; set; }

    public JsonResult Translate(string text)
    {
        return Json(new
        {
            source = text,
            translation = Translator.Translate(text)
        });
    }
}

パズルの最後のピースは、これらのコンポーネント (パーツ) を接着することです (小さなピースから曲全体を構成するため)。したがって、Application_StartWebプロジェクトには次のようなものがあります。

        var parts = new AggregateCatalog
            (
                new DirectoryCatalog(Server.MapPath("/parts")), 
                new DirectoryCatalog(Server.MapPath("/bin"))
            );
        var composer = new CompositionContainer(parts);
        composer.ComposeParts();

これは、 GoogleTranslator.dllおよびBingTranslator.dllファイル (エクスポーターはこれらのファイルにあります)/partsをドロップするフォルダーであり、フォルダーには、インポーターを含むWeb.dllファイルがあります。ただし、私の問題は、MEF がのプロパティに必要なトランスレータを入力しないことです。このサイトで MEF に関連するほとんどすべての質問を読みましたが、私の例の何が問題なのかわかりませんでした。私がここで見逃したものを教えてください。/binTranslatorGeneralController

4

3 に答える 3

9

あなたがする必要があるのはOKです(パフォーマンスを処方することなく、これはそれが機能するのを見るだけです)

public class GeneralController : Controller
{
    [Import]
    public ITranslator Translator { get; set; }

    public JsonResult Translate(string text)
    {
        var container = new CompositionContainer(
        new DirectoryCatalog(Path.Combine(HttpRuntime.BinDirectory, "Plugins")));
        CompositionBatch compositionBatch = new CompositionBatch();
        compositionBatch.AddPart(this);
        Container.Compose(compositionBatch);

        return Json(new
        {
            source = text,
            translation = Translator.Translate(text)
        });
    }
}

私は MEF の専門家ではありません。率直に言って、それを使用する目的についてはあまり役に立ちません。DLL をロードするためだけに使用し、依存関係を注入するためのエントリ ポイントがあり、それ以降は DI を使用するためです。コンテナーであり、MEF ではありません。

私が見た限りでは、MEF は不可欠です。あなたの場合、MEFed にする必要があるもの、つまり controller を積極的に作成する必要がありますしたがって、コントローラー ファクトリはコントローラー インスタンスを作成する必要があります。

MVC アプリで MEFed コンポーネントを使用することはめったにないため、MEF を必要とするアクション用のフィルターを用意しています (コントローラーの facrory ですべてのコントローラーを MEF するのではなく)。

public class InitialisePluginsAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        CompositionBatch compositionBatch = new CompositionBatch();
        compositionBatch.AddPart(filterContext.Controller);
        UniversalCompositionContainer.Current.Container.Compose(
            compositionBatch);
        base.OnActionExecuting(filterContext);
    }
}

UniversalCompositionContainer.Current.Containerこれは、ディレクトリ カタログで初期化されたシングルトン コンテナーです。


MEFに関する私の個人的な見解

MEF は、DI フレームワークではありませんが、多くのことを行います。そのため、DI とは大きな重複があり、既に DI フレームワークを使用している場合、それらは衝突することになります。

MEF は、実行時に DLL をロードする際に強力です。特に、プラグインをロード/アンロードし、他のすべてがそのまま機能し、機能を追加/削除することが期待される WPF アプリがある場合に役立ちます。

Web アプリの場合、実際には動作中の Web アプリケーションに DLL をドロップすることは想定されていないため、これはあまり意味がありません。したがって、その用途は非常に限られています。

ASP.NET MVC のプラグインに関する投稿を作成し、この投稿をリンクで更新します。

于 2012-05-22T11:00:10.453 に答える
5

MEF は、それ自体が構築するオブジェクトのインポートのみを設定します。ASP.NET MVC の場合、コントローラー オブジェクトを作成するのは ASP.NET です。属性が認識されない[Import]ため、依存関係が欠落していることがわかります。

MEF にコントローラーを構築させるには、次の手順を実行する必要があります。

  1. コントローラ クラス自体を でマークし[Export]ます。
  2. MEF コンテナーをラップするIDependencyResolver実装を実装します。MEF コンテナーに一致するエクスポートを要求することで、 GetServiceを実装できます。を使用して、要求された型から MEF コントラクト文字列を生成できますAttributedModelServices.GetContractName
  3. でDependencyResolver.SetResolverを呼び出して、そのリゾルバーを登録しますApplication_Start

[PartCreationPolicy(CreationPolicy.NonShared)]同じインスタンスが複数のリクエストで同時に再利用されるのを防ぐために、エクスポートされたパーツのほとんどを でマークする必要もあります。そうしないと、MEF パーツに保持されているすべての状態が競合状態の対象になります。

編集: このブログ投稿には、手順全体の良い例があります。

edit2 : 別の問題がある可能性があります。MEF コンテナーはIDisposable、コンテナー自体が破棄されるときにそれらのオブジェクトを破棄できるように、作成するすべてのオブジェクトへの参照を保持します。ただし、これは「リクエストごと」の有効期間を持つオブジェクトには適していません! を実装するすべてのサービスでメモリ リークが事実上発生しますIDisposable

ASP.NET MVC 統合用の NuGet パッケージがあり、要求ごとのライフタイムをサポートしているAutoFacのような代替手段を使用する方がおそらく簡単です。

于 2012-05-22T15:10:15.020 に答える
2

@Aliostad が述べたように、コンポジションの初期化コードを機能させるには、コントローラーの作成中または作成後に実行する必要があります。global.asax ファイルに配置するだけでは機能しません。

ただし、例では、検出したバイナリから任意の数の ITranslator 実装を操作できるため、[ImportMany]単に の代わりに使用する必要もあります。[Import]ポイントは、多くITranslatorの があり、それらを単一のインスタンスにインポートしている場合、実際にどの実装が必要かがわからないため、MEF から例外が発生する可能性が高いということです。

したがって、代わりに次を使用します。

[ImportMany]
public IEnumerable<ITranslator> Translator { get; set; }

簡単な例:

http://dotnetbyexample.blogspot.co.uk/2010/04/very-basic-mef-sample-using-importmany.html

于 2012-05-22T13:31:57.407 に答える