Bagaimana Anda melakukan loop melalui rakitan yang saat ini dimuat?
120
Saya mendapat halaman "diagnostik" di aplikasi ASP.NET saya yang melakukan hal-hal seperti memverifikasi koneksi database, menampilkan appSettings dan ConnectionStrings saat ini, dll. Sebuah bagian dari halaman ini menampilkan versi Assembly dari jenis penting yang digunakan di seluruh , tetapi saya tidak tahu cara secara efektif menampilkan versi SEMUA rakitan yang dimuat.
Apa cara paling efektif untuk mengetahui semua Sidang yang direferensikan dan / atau dimuat saat ini dalam aplikasi .NET?
Catatan: Saya tidak tertarik dengan metode berbasis file, seperti melakukan iterasi melalui * .dll di direktori tertentu. Saya tertarik dengan aplikasi yang sebenarnya digunakan sekarang.
Metode ekstensi ini mendapatkan semua rakitan yang direferensikan, secara rekursif, termasuk rakitan bersarang.
Saat digunakan ReflectionOnlyLoad, ia memuat rakitan di AppDomain terpisah, yang memiliki keuntungan karena tidak mengganggu proses JIT.
Anda akan melihat bahwa ada juga file MyGetMissingAssembliesRecursive. Anda dapat menggunakan ini untuk mendeteksi rakitan yang hilang yang direferensikan, tetapi tidak ada di direktori saat ini karena beberapa alasan. Ini sangat berguna saat menggunakan MEF . Daftar pengembalian akan memberi Anda rakitan yang hilang, dan siapa yang memilikinya (induknya).
/// <summary>/// Intent: Get referenced assemblies, either recursively or flat. Not thread safe, if running in a multi/// threaded environment must use locks./// </summary>publicstaticclassGetReferencedAssemblies{staticvoidDemo(){var referencedAssemblies =Assembly.GetEntryAssembly().MyGetReferencedAssembliesRecursive();var missingAssemblies =Assembly.GetEntryAssembly().MyGetMissingAssembliesRecursive();// Can use this within a class.//var referencedAssemblies = this.MyGetReferencedAssembliesRecursive();}publicclassMissingAssembly{publicMissingAssembly(string missingAssemblyName,string missingAssemblyNameParent){MissingAssemblyName= missingAssemblyName;MissingAssemblyNameParent= missingAssemblyNameParent;}publicstringMissingAssemblyName{get;set;}publicstringMissingAssemblyNameParent{get;set;}}privatestaticDictionary<string,Assembly> _dependentAssemblyList;privatestaticList<MissingAssembly> _missingAssemblyList;/// <summary>/// Intent: Get assemblies referenced by entry assembly. Not recursive./// </summary>publicstaticList<string>MyGetReferencedAssembliesFlat(thisType type){var results = type.Assembly.GetReferencedAssemblies();return results.Select(o => o.FullName).OrderBy(o => o).ToList();}/// <summary>/// Intent: Get assemblies currently dependent on entry assembly. Recursive./// </summary>publicstaticDictionary<string,Assembly>MyGetReferencedAssembliesRecursive(thisAssembly assembly){
_dependentAssemblyList =newDictionary<string,Assembly>();
_missingAssemblyList =newList<MissingAssembly>();InternalGetDependentAssembliesRecursive(assembly);// Only include assemblies that we wrote ourselves (ignore ones from GAC).var keysToRemove = _dependentAssemblyList.Values.Where(
o => o.GlobalAssemblyCache==true).ToList();foreach(var k in keysToRemove){
_dependentAssemblyList.Remove(k.FullName.MyToName());}return _dependentAssemblyList;}/// <summary>/// Intent: Get missing assemblies./// </summary>publicstaticList<MissingAssembly>MyGetMissingAssembliesRecursive(thisAssembly assembly){
_dependentAssemblyList =newDictionary<string,Assembly>();
_missingAssemblyList =newList<MissingAssembly>();InternalGetDependentAssembliesRecursive(assembly);return _missingAssemblyList;}/// <summary>/// Intent: Internal recursive class to get all dependent assemblies, and all dependent assemblies of/// dependent assemblies, etc./// </summary>privatestaticvoidInternalGetDependentAssembliesRecursive(Assembly assembly){// Load assemblies with newest versions first. Omitting the ordering results in false positives on// _missingAssemblyList.var referencedAssemblies = assembly.GetReferencedAssemblies().OrderByDescending(o => o.Version);foreach(var r in referencedAssemblies){if(String.IsNullOrEmpty(assembly.FullName)){continue;}if(_dependentAssemblyList.ContainsKey(r.FullName.MyToName())==false){try{var a =Assembly.ReflectionOnlyLoad(r.FullName);
_dependentAssemblyList[a.FullName.MyToName()]= a;InternalGetDependentAssembliesRecursive(a);}catch(Exception ex){
_missingAssemblyList.Add(newMissingAssembly(r.FullName.Split(',')[0], assembly.FullName.MyToName()));}}}}privatestaticstringMyToName(thisstring fullName){return fullName.Split(',')[0];}}
Memperbarui
Untuk membuat utas kode ini aman, letakkan lockdi sekitarnya. Saat ini tidak aman utas secara default, karena merujuk variabel global statis bersama untuk melakukan keajaibannya.
Saya baru saja menulis ulang ini agar aman untuk utas, sehingga dapat dipanggil dari banyak utas berbeda secara bersamaan (tidak yakin mengapa Anda menginginkannya, tapi hei, ini lebih aman). Beri tahu saya jika Anda ingin saya memposting kode.
Contango
2
@Contango Bisakah Anda memposting versi aman Thread, atau jika Anda telah menulis blog tentangnya, posting itu?
Robert
2
Cara naif untuk membuat utas ini aman adalah dengan meletakkan locksemuanya. Metode lain yang saya gunakan menghilangkan ketergantungan pada statis global "_dependentAssemblyList", sehingga menjadi thread aman tanpa memerlukan lock, yang memiliki sedikit keuntungan kecepatan jika beberapa thread mencoba untuk secara bersamaan menentukan rakitan apa yang hilang (ini sedikit kasus sudut).
Contango
3
menambahkan a locktidak akan menambah banyak cara "aman utas". Tentu, itu membuat blok kode itu hanya dieksekusi satu per satu; tetapi utas lain dapat memuat rakitan kapan saja mereka suka dan itu dapat menyebabkan masalah dengan beberapa foreachloop.
Peter Ritchie
1
@Peter Ritchie Ada variabel global statis bersama yang digunakan selama rekursi, jadi menambahkan kunci di sekitar semua akses akan membuat utas bagian itu aman. Itu hanya latihan pemrograman yang bagus. Biasanya semua rakitan yang diperlukan dimuat saat startup, kecuali sesuatu seperti MEF digunakan, jadi keamanan thread sebenarnya tidak menjadi masalah dalam praktiknya.
Contango
193
Mendapatkan rakitan yang dimuat untuk arus AppDomain:
var loadedAssemblies =AppDomain.CurrentDomain.GetAssemblies();
Mendapatkan majelis yang direferensikan oleh majelis lain:
var referencedAssemblies = someAssembly.GetReferencedAssemblies();
Perhatikan bahwa jika perakitan A referensi perakitan B dan perakitan A dimuat, itu tidak berarti bahwa perakitan B juga dimuat. Majelis B hanya akan dimuat jika dan saat dibutuhkan. Oleh karena itu, GetReferencedAssemblies()mengembalikan AssemblyNameinstance, bukan Assemblyinstance.
Yah saya butuh sesuatu seperti ini - mengingat solusi bersih. Saya ingin mengetahui semua rakitan yang direferensikan di semua proyek. Ide ide?
Kumar Vaibhav
Harap dicatat bahwa kedua metode hanya mencantumkan dll yang benar-benar digunakan. Jelas tidak masuk akal untuk memiliki referensi dalam solusi yang tidak digunakan, tetapi ini mungkin membingungkan ketika seseorang mencoba untuk memindai secara spekulatif melalui SEMUA rakitan. Semua majelis mungkin tidak muncul begitu saja.
Pompair
3
OP meminta majelis yang dimuat saat ini bukan majelis yang direferensikan. Ini menjawab pertanyaan itu. Persis apa yang saya cari.
lock
semuanya. Metode lain yang saya gunakan menghilangkan ketergantungan pada statis global "_dependentAssemblyList", sehingga menjadi thread aman tanpa memerlukanlock
, yang memiliki sedikit keuntungan kecepatan jika beberapa thread mencoba untuk secara bersamaan menentukan rakitan apa yang hilang (ini sedikit kasus sudut).lock
tidak akan menambah banyak cara "aman utas". Tentu, itu membuat blok kode itu hanya dieksekusi satu per satu; tetapi utas lain dapat memuat rakitan kapan saja mereka suka dan itu dapat menyebabkan masalah dengan beberapaforeach
loop.Mendapatkan rakitan yang dimuat untuk arus
AppDomain
:Mendapatkan majelis yang direferensikan oleh majelis lain:
Perhatikan bahwa jika perakitan A referensi perakitan B dan perakitan A dimuat, itu tidak berarti bahwa perakitan B juga dimuat. Majelis B hanya akan dimuat jika dan saat dibutuhkan. Oleh karena itu,
GetReferencedAssemblies()
mengembalikanAssemblyName
instance, bukanAssembly
instance.sumber