Saya ingin memuat ke AppDomain
beberapa rakitan baru yang memiliki pohon referensi kompleks (MyDll.dll -> Microsoft.Office.Interop.Excel.dll -> Microsoft.Vbe.Interop.dll -> Office.dll -> stdole.dll)
Sejauh yang saya mengerti, ketika sebuah assembly sedang dimuat AppDomain
, referensinya tidak akan dimuat secara otomatis, dan saya harus memuatnya secara manual. Jadi ketika saya melakukannya:
string dir = @"SomePath"; // different from AppDomain.CurrentDomain.BaseDirectory
string path = System.IO.Path.Combine(dir, "MyDll.dll");
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
setup.ApplicationBase = dir;
AppDomain domain = AppDomain.CreateDomain("SomeAppDomain", null, setup);
domain.Load(AssemblyName.GetAssemblyName(path));
dan mendapatkan FileNotFoundException
:
Tidak dapat memuat file atau assembly 'MyDll, Version = 1.0.0.0, Culture = neutral, PublicKeyToken = null' atau salah satu dependensinya. Sistem tidak dapat menemukan berkas yang dicari.
Saya pikir bagian kuncinya adalah salah satu ketergantungannya .
Oke, saya lakukan selanjutnya sebelumnya domain.Load(AssemblyName.GetAssemblyName(path));
foreach (AssemblyName refAsmName in Assembly.ReflectionOnlyLoadFrom(path).GetReferencedAssemblies())
{
domain.Load(refAsmName);
}
Tapi mendapat FileNotFoundException
lagi, di majelis (direferensikan) lain.
Bagaimana cara memuat semua referensi secara rekursif?
Apakah saya harus membuat pohon referensi sebelum memuat perakitan root? Bagaimana cara mendapatkan referensi majelis tanpa memuatnya?
sumber
Jawaban:
Anda perlu memanggil
CreateInstanceAndUnwrap
sebelum objek proxy Anda akan dieksekusi di domain aplikasi asing.Juga, perhatikan bahwa jika Anda menggunakan
LoadFrom
Anda kemungkinan besar akan mendapatkanFileNotFound
pengecualian karena resolver Majelis akan mencoba menemukan rakitan yang Anda muat di GAC atau folder bin aplikasi saat ini. GunakanLoadFile
untuk memuat file assembly arbitrary sebagai gantinya - tetapi perhatikan bahwa jika Anda melakukan ini, Anda harus memuat sendiri dependensi apa pun.sumber
AppDomain.CurrentDomain.AssemblyResolve
acara seperti yang dijelaskan dalam jawaban MSDN ini . Dalam kasus saya, saya mencoba untuk menghubungkan ke penerapan SpecRun yang berjalan di bawah MSTest, tetapi saya pikir itu berlaku untuk banyak situasi di mana kode Anda mungkin tidak berjalan dari AppDomain "utama" - ekstensi VS, MSTest, dll.assembly
variabel akan mereferensikan perakitan dari "MyDomain"? Saya pikir olehvar assembly = value.GetAssembly(args[0]);
Anda akan memuat Andaargs[0]
ke kedua domain danassembly
variabel akan merujuk salinan dari domain aplikasi utamahttp://support.microsoft.com/kb/837908/en-us
Versi C #:
Buat kelas moderator dan wariskan dari
MarshalByRefObject
:panggilan dari situs klien
sumber
MarshalByRefObject
dapat diteruskan ke appdomains. Jadi saya akan menebak bahwaAssembly.LoadFrom
mencoba memuat rakitan di appdomain baru, apa yang hanya mungkin, jika objek pemanggil dapat dilewatkan di antara appdomains tersebut. Ini juga disebut remoting seperti yang dijelaskan di sini: msdn.microsoft.com/en-us/library/…MarshalByRefObject
tidak secara ajaib membuatnya dimuat di satu sama lainAppDomain
, itu hanya memberi tahu kerangka .NET untuk membuat proxy jarak jauh transparan alih-alih menggunakan serialisasi ketika Anda membuka referensi dari satuAppDomain
sama lainAppDomain
(cara yang khas adalahCreateInstanceAndUnwrap
metode). Tidak percaya jawaban ini memiliki lebih dari 30 suara positif; kode di sini hanyalah cara menelepon yang berputar-putarAssembly.LoadFrom
.Setelah Anda meneruskan instance assembly kembali ke domain pemanggil, domain pemanggil akan mencoba memuatnya! Inilah mengapa Anda mendapatkan pengecualian. Ini terjadi di baris terakhir kode Anda:
Jadi, apa pun yang ingin Anda lakukan dengan perakitan, harus dilakukan di kelas proxy - kelas yang mewarisi MarshalByRefObject .
Perhatikan bahwa domain pemanggil dan domain yang baru dibuat harus memiliki akses ke rakitan kelas proxy. Jika masalah Anda tidak terlalu rumit, pertimbangkan untuk membiarkan folder ApplicationBase tidak berubah, sehingga akan sama dengan folder domain pemanggil (domain baru hanya akan memuat Assemblies yang dibutuhkannya).
Dalam kode sederhana:
Jika Anda perlu memuat rakitan dari folder yang berbeda dari folder domain aplikasi Anda saat ini, buat domain aplikasi baru dengan folder jalur pencarian dll tertentu.
Misalnya, baris pembuatan domain aplikasi dari kode di atas harus diganti dengan:
Dengan cara ini, semua dll secara otomatis akan diselesaikan dari dllsSearchPath.
sumber
Di AppDomain baru Anda, coba tetapkan penangan peristiwa AssemblyResolve . Peristiwa itu dipanggil saat dependensi hilang.
sumber
Anda perlu menangani peristiwa AppDomain.AssemblyResolve atau AppDomain.ReflectionOnlyAssemblyResolve (tergantung pada pemuatan yang Anda lakukan) jika rakitan yang direferensikan tidak ada di GAC atau di jalur probing CLR.
AppDomain.AssemblyResolve
AppDomain.ReflectionOnlyAssemblyResolve
sumber
Butuh beberapa saat bagi saya untuk memahami jawaban @ user1996230, jadi saya memutuskan untuk memberikan contoh yang lebih eksplisit. Dalam contoh di bawah ini saya membuat proxy untuk objek yang dimuat di AppDomain lain dan memanggil metode pada objek itu dari domain lain.
sumber
Kuncinya adalah event AssemblyResolve yang dimunculkan oleh AppDomain.
sumber
Saya harus melakukan ini beberapa kali dan telah meneliti banyak solusi berbeda.
Solusi yang saya temukan paling elegan dan mudah dicapai dapat diterapkan seperti itu.
1. Buat proyek yang Anda dapat membuat antarmuka sederhana
antarmuka akan berisi tanda tangan dari setiap anggota yang ingin Anda panggil.
Sangat penting untuk menjaga proyek ini tetap bersih dan ringan. Ini adalah proyek yang
AppDomain
dapat menjadi referensi dan memungkinkan kita untuk tidak merujukAssembly
pada domain yang ingin kita muat dalam domain terpisah dari perakitan klien kita.2. Sekarang buat proyek yang memiliki kode yang ingin Anda muat secara terpisah
AppDomain
.Proyek ini seperti proyek klien akan mereferensikan proj proxy dan Anda akan mengimplementasikan antarmuka.
3. Selanjutnya, dalam proyek klien, muat kode lain
AppDomain
.Jadi, sekarang kita buat yang baru
AppDomain
. Dapat menentukan lokasi dasar untuk referensi perakitan. Probing akan memeriksa rakitan dependen di GAC dan di direktori saat ini dan lokasiAppDomain
basis.jika perlu, ada banyak cara berbeda untuk memuat rakitan. Anda dapat menggunakan cara berbeda dengan solusi ini. Jika Anda memiliki nama yang memenuhi syarat rakitan maka saya suka menggunakan
CreateInstanceAndUnwrap
karena memuat byte rakitan dan kemudian memberi contoh jenis Anda untuk Anda dan mengembalikanobject
yang Anda dapat dengan mudah dilemparkan ke jenis proxy Anda atau jika Anda tidak memasukkannya ke dalam kode yang diketik dengan kuat Anda bisa gunakan runtime bahasa dinamis dan tetapkan objek yang dikembalikan kedynamic
variabel yang diketik, lalu panggil anggotanya secara langsung.Itu dia.
Hal ini memungkinkan untuk memuat rakitan yang proyek klien Anda tidak memiliki referensi secara terpisah
AppDomain
dan memanggil anggota di dalamnya dari klien.Untuk menguji, saya suka menggunakan jendela Modul di Visual Studio. Ini akan menunjukkan domain rakitan klien Anda dan semua modul yang dimuat di domain itu serta domain aplikasi baru Anda dan rakitan atau modul apa yang dimuat di domain itu.
Kuncinya adalah memastikan kode Anda diturunkan
MarshalByRefObject
atau dapat diserialkan.`MarshalByRefObject akan memungkinkan Anda untuk mengkonfigurasi masa pakai domain yang masuk. Contoh, katakanlah Anda ingin domain dihancurkan jika proxy belum dipanggil dalam 20 menit.
Saya harap ini membantu.
sumber
Foo, FooAssembly
yang memiliki properti tipeBar, BarAssembly
, yaitu total 3 majelis. Apakah itu akan terus bekerja?