6

mef を使用して POC を作成しようとしています。この場合、すべての準備が整った実行中のプロジェクトで dll を動的にロードする必要があります。このため、1 つのコンソール アプリケーション プロジェクトとクラス ライブラリを作成しました。

事業 。

コンソール アプリケーション プロジェクトのコードは次のとおりです。

namespace MefProjectExtension
{
    class Program
    {
        DirectoryCatalog catalog = new DirectoryCatalog(@"D:\MefDll", "*.dll");

        [Import("Method1", AllowDefault = true, AllowRecomposition = true)]
        public Func<string> method1;

        static void Main(string[] args)
        {
            AppDomainSetup asp = new AppDomainSetup();
            asp.ShadowCopyFiles = "true";

            AppDomain sp = AppDomain.CreateDomain("sp",null,asp);

            string exeassembly = Assembly.GetEntryAssembly().ToString();
            BaseClass p = (BaseClass)sp.CreateInstanceAndUnwrap(exeassembly, "MefProjectExtension.BaseClass");
            p.run();
        }
    }


    public class BaseClass : MarshalByRefObject
    {
        [Import("Method1",AllowDefault=true,AllowRecomposition=true)]
        public Func<string> method1;

        DirectoryCatalog catalog = new DirectoryCatalog(@"D:\MefDll", "*.dll");

        public void run()
        {
            FileSystemWatcher sw = new FileSystemWatcher(@"D:\MefDll", "*.dll");
            sw.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.Size;
            sw.Changed += onchanged;

            CompositionContainer container = new CompositionContainer(catalog);

            container.ComposeParts(this);

            Console.WriteLine(this.method1());

            sw.EnableRaisingEvents = true;

            Console.Read();
        }

        void onchanged(object sender, FileSystemEventArgs e)
        {
            catalog.Refresh();

            Console.WriteLine(this.method1());
        }
    }
}

インポートを満たすライブラリ プロジェクトは次のようになります。

namespace MefLibrary
{
    public interface IMethods
    {
         string Method1();  
    }

    public class CallMethods : IMethods
    {
        [Export("Method1")]
        public string Method1()
        {
            return "Third6Hello";
        }
    }
}

ライブラリ プロジェクト (MefLibrary) をコンパイルし、DLL を D:\MefDll の場所に置き、コンソール アプリケーションを初めて実行すると、次のような出力が表示されます。

画面上のThird6hello

しかし今、method1 の実装を変更して "third7hello" を返すようにすると、MEF ライブラリ プロジェクトをビルドし、D:\MefDll で置き換えます。これは、コンソール アプリが onchanged ハンドラーを実行しているときに、カタログの更新を呼び出した後 でも、画面にthird7hello ではなくThird6helloが出力されます。

誰かがこれの理由を知っているかどうか、もしそうなら助けてください。

4

2 に答える 2

9

DirectoryCatalog.Refresh新しいアセンブリを追加するか、既存のアセンブリを削除するだけです。アセンブリは更新されません。大まかな回避策は次のとおりです。

  1. 更新されたアセンブリを一時フォルダーに移動します。
  2. コールしDirectoryCatalog.Refreshます。これにより、アセンブリに含まれるパーツが削除されます。
  3. アセンブリを監視フォルダーに戻します
  4. コールしDirectoryCatalog.Refreshます。これにより、アセンブリに含まれる更新されたパーツが追加されます。

ノート:

  • これを機能させるには、「プラグイン」アセンブリに厳密な名前を付け、異なるバージョン番号( AssemblyVersionAttribute) を付ける必要があります。これが必要なのはDirectoryCatalog.Refresh、実際のアセンブリを使用してパーツを削除すると、アンロードされないためです。アセンブリは、アプリケーション ドメイン全体がアンロードされている場合にのみアンロードできます。したがってDirectoryCatalog.Refresh、新しいアセンブリが見つかった場合はAssemblyCatalog、アセンブリ ファイルパスを使用して作成されます。次に、アセンブリをロードするためAssemblyCatalogに呼び出します。Assembly.Loadただし、このメソッドはAssemblyName.FullName、既に読み込まれているアセンブリと同じアセンブリを読み込みません。
  • 私が言及した手順が別の FileSystemWatcher.Changed イベントをトリガーしないことを確認してください。たとえば、このアプローチを使用できます。
  • プログラムには、監視フォルダーへの書き込みアクセス権が必要です。これは、%ProgramFiles% フォルダーに展開する場合に問題になる可能性があります。
  • スレッドセーフが必要な場合は、CompositionOption.IsThreadSafeフラグを使用して CompositionContainer を作成することを検討できます。

前述したように、これは回避策です。もう 1 つの方法は、MEF のソース コードをダウンロードし、DirectoryCatalog.cs を独自のディレクトリ カタログ実装のガイドラインとして使用することです。

于 2013-02-12T21:44:37.890 に答える
1

DLL がアプリ ドメインに読み込まれると、そのドメインからアンロードできなくなります。ドメイン全体のみをアンロードできます。そのため、求めているものを実装するのは簡単ではありません。常に変更をスキャンして新しいコピーをロードし、呼び出しを再指定することは可能です (この方法では、ドメイン内に無駄なアセンブリがますます蓄積されます)、MEF がすぐに実装できるものではないと思います. つまり、観察している動作は設計によるものです。

これの実装も、状態のためにトリッキーでバグが発生しやすい可能性があります。古い DLL のクラス インスタンスにフィールドを設定し、それを変数に割り当てるとします。次に、新しい dll が通過します。古いインスタンスとそのフィールドはどうなりますか? どうやらそれらは同じままで、メモリ内で使用されているプラ​​グインのバージョンが異なるようです。なんて混乱!

興味がある場合は、Assembly.Unload メソッドがない理由をここで説明します。そして可能な(概念的な)回避策

于 2013-02-12T21:19:21.747 に答える