4

すでに MEF Codeplex フォーラムで質問しましたが、まだ回答が得られていないため、StackOverflow を試してみることにしました。誰かが興味を持っている場合は、元の投稿を次に示します (これは単なるコピーです)。

MEFコードプレックス

「最初に言っておきますが、私は MEF をまったく初めて使用し (今日発見したばかりです)、これまでのところ非常に満足しています。しかし、非常にイライラする問題に遭遇しました。私は、そのアプリを作成しています。にはプラグイン アーキテクチャがあり、プラグインは単一の DLL ファイルにのみ保存されます (またはメイン アプリにコード化されます)。DLL ファイルは実行時に再コンパイルできる必要があり、アプリはこれを認識して再読み込みする必要があります。プラグイン(これは難しいことはわかっていますが、要件です)これを達成するために、http://blog.maartenballiauw.be/category/MEF.aspxで説明されているアプローチを取りましたあります (WebServerDirectoryCatalog を探します)。基本的な考え方は、「プラグイン フォルダーを監視し、新しい/変更されたアセンブリを Web アプリケーションの /bin フォルダーにコピーし、そこからエクスポートを読み込むように MEF に指示する」ことです。これは私のコードです。これはおそらく正しい方法ではありませんが、ネット上のいくつかのサンプルで見つけたものです。

        main()...
    string myExecName = Assembly.GetExecutingAssembly().Location;
        string myPath = System.IO.Path.GetDirectoryName(myExecName);
        catalog = new AggregateCatalog();
        pluginCatalog = new MyDirectoryCatalog(myPath + @"/Plugins");
        catalog.Catalogs.Add(pluginCatalog);


        exportContainer = new CompositionContainer(catalog);

        CompositionBatch compBatch = new CompositionBatch();
        compBatch.AddPart(this);
        compBatch.AddPart(catalog);
        exportContainer.Compose(compBatch);

    private FileSystemWatcher fileSystemWatcher;
    public DirectoryCatalog directoryCatalog;
    private string path;
    private string extension;

    public MyDirectoryCatalog(string path)
    {
        Initialize(path, "*.dll", "*.dll");
    }

    private void Initialize(string path, string extension, string modulePattern)
    {
        this.path = path;
        this.extension = extension;
        fileSystemWatcher = new FileSystemWatcher(path, modulePattern);
        fileSystemWatcher.Changed += new FileSystemEventHandler(fileSystemWatcher_Changed);
        fileSystemWatcher.Created += new FileSystemEventHandler(fileSystemWatcher_Created);
        fileSystemWatcher.Deleted += new FileSystemEventHandler(fileSystemWatcher_Deleted);
        fileSystemWatcher.Renamed += new RenamedEventHandler(fileSystemWatcher_Renamed);
        fileSystemWatcher.IncludeSubdirectories = false;
        fileSystemWatcher.EnableRaisingEvents = true;
        Refresh();
    }
    void fileSystemWatcher_Renamed(object sender, RenamedEventArgs e)
    {
        RemoveFromBin(e.OldName);
        Refresh();
    }
    void fileSystemWatcher_Deleted(object sender, FileSystemEventArgs e)
    {
        RemoveFromBin(e.Name);
        Refresh();
    }
    void fileSystemWatcher_Created(object sender, FileSystemEventArgs e)
    {
        Refresh();
    }
    void fileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
    {
        Refresh();
    }
    private void Refresh()
    {
        // Determine /bin path 
        string binPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins");
        string newPath = "";
        // Copy files to /bin 
        foreach (string file in Directory.GetFiles(path, extension, SearchOption.TopDirectoryOnly))
        {
            try
            {
                DirectoryInfo dInfo = new DirectoryInfo(binPath);
                DirectoryInfo[] dirs = dInfo.GetDirectories();
                int count = dirs.Count() + 1;
                newPath = binPath + "/" + count;
                DirectoryInfo dInfo2 = new DirectoryInfo(newPath);
                if (!dInfo2.Exists)
                    dInfo2.Create();

                File.Copy(file, System.IO.Path.Combine(newPath, System.IO.Path.GetFileName(file)), true);
            }
            catch
            {
                // Not that big deal... Blog readers will probably kill me for this bit of code :-) 
            }
        }
        // Create new directory catalog 
        directoryCatalog = new DirectoryCatalog(newPath, extension);
        directoryCatalog.Refresh();
    }
    public override IQueryable<ComposablePartDefinition> Parts
    {
        get { return directoryCatalog.Parts; }
    }
    private void RemoveFromBin(string name)
    {
        string binPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "");
        File.Delete(Path.Combine(binPath, name));
    }

したがって、これはすべて実際に機能し、メインのコードの終了後、実際に IEnumerable 変数が DLL 内のすべてのプラグインで満たされます (コードに従えば、プラグイン/1 にあるので、内の dll を変更できます)。プラグイン フォルダー)。この時点で、プラグイン DLL を再コンパイルして Plugins フォルダーにドロップすると、FileWatcher が変更されたことを検出し、それをフォルダー "2" にコピーすると、directoryCatalog が新しいフォルダーを指す必要があります。これはすべて実際に機能します!問題は、すべてが正しい場所を指しているように見えても、IEnumerable 変数が新しいプラグインで更新されないことです。とても近いですが、まだ遠いです!助言がありますか?この方法で実行することのマイナス面は知っていますが、実際には dll がアンロードされず、メモリ リークが発生することはありませんが、' sa Windows アプリであり、おそらく少なくとも 1 日に 1 回は開始され、プラグインはそれほど頻繁に変更されることはありませんが、アプリを再ロードせずにこれを行うことがクライアントからの要件であることに変わりはありません。ありがとう!

皆さんが提供できるあらゆる助けに感謝します。これを理解できないことに私は頭がおかしくなりました。」

4

3 に答える 3

3

カタログの実装では通知が提供されないため、再構成のトリガーはありません。これを修正するには、INotifyComposablePartCatalogChangedを実装します。

于 2010-04-12T22:29:49.490 に答える
1

私は同様の問題を抱えていました-発見されたプラグインをアプリケーションのディレクトリにコピーした後、DirectoryCatalog で .refresh() を呼び出した後でも、DirectoryCatalog はそれらを認識しませんでした。

コードをステップ実行することで問題が解決したことがわかりました.MEFが新しいアセンブリをスキャンし(おそらくいくつかのあいまいなコピー操作を完了するため)、内部のパーツを確認できるようになる前に、FileSystemWatcherが通知を開始した後、ファイルシステムにはまだ少し時間が必要であると思います. .

System.Threading.Thread.Sleep(1000) は、問題を解決しました。

于 2011-08-11T18:45:57.413 に答える
1

MEF は同じアセンブリの 1 つのバージョンしかロードできないと思います (ただし、Silverlight を試していました)。

于 2010-04-13T08:22:38.157 に答える