Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Nota
Artikel ini khusus untuk .NET Framework. Ini tidak berlaku untuk implementasi .NET yang lebih baru, termasuk .NET 6 dan versi yang lebih baru.
Artikel ini membahas cara untuk menghindari masalah jenis identitas yang dapat menyebabkan InvalidCastException, MissingMethodException, dan kesalahan lainnya. Artikel ini membahas rekomendasi berikut:
Rekomendasi pertama, memahami kelebihan dan kekurangan konteks beban, memberikan informasi latar belakang untuk rekomendasi lain, karena semuanya bergantung pada pengetahuan tentang konteks beban.
Memahami Kelebihan dan Kekurangan Konteks Beban
Dalam domain aplikasi, rakitan dapat dimuat ke dalam salah satu dari tiga konteks, atau dapat dimuat tanpa konteks:
Konteks muatan bawaan berisi rakitan yang ditemukan dengan menelusuri cache rakitan global, penyimpanan rakitan host jika runtime dihosting (misalnya, di SQL Server), serta ApplicationBase dan PrivateBinPath domain aplikasi. Sebagian besar overload metode Load memuat assembly ke dalam konteks ini.
Konteks 'load-from' berisi assembly yang dimuat dari lokasi yang tidak dicari oleh pemuat. Misalnya, add-in mungkin diinstal di direktori yang tidak berada di bawah jalur aplikasi. Assembly.LoadFrom, AppDomain.CreateInstanceFrom, dan AppDomain.ExecuteAssembly adalah contoh metode yang dimuat menurut jalur.
Konteks khusus refleksi berisi rakitan yang dimuat dengan metode ReflectionOnlyLoad dan ReflectionOnlyLoadFrom. Kode dalam konteks ini tidak dapat dijalankan, sehingga tidak dibahas di sini. Untuk informasi selengkapnya, baca Cara: Memuat Assembly ke dalam Konteks Reflection-Only.
Jika Anda membuat rakitan dinamis transien dengan menggunakan emisi refleksi, rakitan tersebut tidak berada dalam konteks apa pun. Selain itu, sebagian besar rakitan yang dimuat dengan menggunakan metode LoadFile dimuat tanpa konteks, dan rakitan yang dimuat dari array-byte juga dimuat tanpa konteks kecuali identitas mereka (setelah kebijakan diterapkan pada mereka) menetapkan bahwa mereka berada dalam cache perakitan global.
Konteks eksekusi memiliki kelebihan dan kekurangan, seperti yang dibahas di bagian berikut.
Konteks Muatan Default
Ketika rakitan dimuat ke dalam konteks beban default, dependensinya dimuat secara otomatis. Dependensi yang dimuat ke dalam konteks beban default ditemukan secara otomatis untuk rakitan dalam konteks beban default atau konteks load-from. Pemuatan dengan identitas perakitan meningkatkan stabilitas aplikasi dengan memastikan bahwa versi rakitan yang tidak diketahui tidak digunakan (lihat bagian Hindari Pengikatan pada Nama Rakitan Parsial ).
Menggunakan konteks beban default memiliki kerugian berikut:
Dependensi yang dimuat ke dalam konteks lain tidak tersedia.
Anda tidak dapat memuat rakitan dari lokasi di luar jalur pemeriksaan ke konteks beban default.
Load-From Konteks
Konteks load-from memungkinkan Anda memuat assembly dari jalur yang tidak berada di bawah jalur aplikasi, dan oleh karena itu tidak termasuk dalam pemeriksaan. Ini memungkinkan dependensi untuk ditemukan dan dimuat dari jalur tersebut, karena informasi jalur dikelola oleh konteks. Selain itu, rakitan dalam konteks ini dapat menggunakan dependensi yang dimuat ke dalam konteks beban default.
Memuat assembly dengan menggunakan metode Assembly.LoadFrom, atau salah satu metode lain yang dimuat berdasarkan jalur, memiliki kekurangan sebagai berikut:
Jika rakitan dengan identitas yang sama sudah dimuat dalam konteks load-from, LoadFrom mengembalikan rakitan yang dimuat bahkan jika jalur yang berbeda ditentukan.
Jika rakitan dimuat dengan LoadFrom, dan kemudian rakitan dalam konteks beban default mencoba memuat rakitan yang sama dengan nama tampilan, upaya pemuatan gagal. Ini dapat terjadi ketika rakitan dideserialisasi.
Jika rakitan dimuat dengan LoadFrom, dan jalur pemeriksaan menyertakan rakitan dengan identitas yang sama tetapi di lokasi yang berbeda, InvalidCastException, MissingMethodException, atau perilaku tak terduga lainnya dapat terjadi.
LoadFrom permintaan FileIOPermissionAccess.Read dan FileIOPermissionAccess.PathDiscovery, atau WebPermission, pada jalur yang ditentukan.
Jika ada gambar asli untuk rakitan, gambar tersebut tidak digunakan.
Rakitan tidak dapat dimuat sebagai domain netral.
Dalam .NET Framework versi 1.0 dan 1.1, kebijakan tidak diterapkan.
Tidak Ada Konteks
Memuat tanpa adanya konteks adalah satu-satunya opsi untuk rakitan sementara yang dihasilkan dengan menggunakan reflection emit. Memuat tanpa konteks adalah satu-satunya cara untuk memuat beberapa rakitan yang memiliki identitas yang sama ke dalam satu domain aplikasi. Biaya penyelidikan dihindari.
Rakitan yang dimuat dari array byte dimuat tanpa konteks kecuali identitas dari assembly tersebut, yang ditetapkan ketika kebijakan diterapkan, cocok dengan identitas sebuah assembly di cache assembly global; dalam hal ini, assembly dimuat dari cache assembly global.
Memuat rakitan tanpa konteks memiliki kerugian berikut:
Rakitan lain tidak dapat mengikat ke rakitan yang dimuat tanpa konteks, kecuali Anda menangani acara AppDomain.AssemblyResolve.
Dependensi tidak dimuat secara otomatis. Anda dapat memuatnya terlebih dahulu tanpa konteks, memuatnya ke dalam konteks pemuatan default, atau memuatnya dengan menangani acara AppDomain.AssemblyResolve.
Memuat beberapa rakitan dengan identitas yang sama tanpa konteks dapat menyebabkan masalah identitas jenis yang mirip dengan yang disebabkan oleh pemuatan rakitan dengan identitas yang sama ke dalam beberapa konteks. Lihat Hindari Memuat Rakitan ke dalam Beberapa Konteks.
Jika ada gambar asli untuk rakitan, gambar tersebut tidak digunakan.
Rakitan tidak dapat dimuat sebagai domain netral.
Dalam .NET Framework versi 1.0 dan 1.1, kebijakan tidak diterapkan.
Hindari Pengikatan pada Nama Rakitan Parsial
Pengikatan nama parsial terjadi saat Anda hanya menentukan bagian dari nama tampilan rakitan (FullName) saat Anda memuat rakitan. Misalnya, Anda mungkin memanggil metode Assembly.Load hanya dengan nama sederhana dari assembly, menghilangkan versi, budaya, dan token kunci publik. Atau Anda mungkin memanggil metode Assembly.LoadWithPartialName, yang pertama-tama memanggil metode Assembly.Load dan, jika itu gagal menemukan assembly, mencari cache perakitan global dan memuat versi assembly terbaru yang tersedia.
Pengikatan nama parsial dapat menyebabkan banyak masalah, termasuk yang berikut ini:
Metode ini Assembly.LoadWithPartialName mungkin memuat rakitan yang berbeda dengan nama sederhana yang sama. Misalnya, dua aplikasi mungkin menginstal dua rakitan yang sama sekali berbeda yang keduanya memiliki nama
GraphicsLibrary
sederhana ke dalam cache perakitan global.Rakitan yang dimuat mungkin tidak kompatibel dengan versi sebelumnya. Misalnya, tidak menentukan versi dapat mengakibatkan pemuatan versi yang jauh lebih baru daripada versi yang awalnya ditulis program Anda untuk digunakan. Perubahan dalam versi yang lebih baru dapat menyebabkan kesalahan dalam aplikasi Anda.
Rakitan yang benar-benar dimuat mungkin tidak kompatibel ke depan. Misalnya, Anda mungkin telah membangun dan menguji aplikasi Anda dengan versi terbaru assembly, tetapi pengikatan parsial mungkin memuat versi yang jauh lebih lama yang tidak memiliki fitur yang digunakan aplikasi Anda.
Menginstal aplikasi baru dapat mengganggu aplikasi yang sudah ada. Aplikasi yang menggunakan metode LoadWithPartialName dapat rusak dengan menginstal versi assembly bersama yang lebih baru dan tidak kompatibel.
Ketidakdugaan dalam pemuatan dependensi dapat terjadi. Jika Anda memuat dua rakitan yang berbagi dependensi, memuatnya dengan pengikatan parsial dapat mengakibatkan satu rakitan menggunakan komponen yang tidak dibangun atau diuji bersamanya.
Karena masalah yang dapat menyebabkannya, LoadWithPartialName metode telah ditandai usang. Kami menyarankan agar Anda menggunakan Assembly.Load sebagai gantinya, dan menentukan nama tampilan rakitan lengkap. Lihat Pahami Keuntungan dan Kekurangan Konteks Pemanggilan dan Pertimbangkan Beralih ke Konteks Pemanggilan default.
Jika Anda ingin menggunakan metode LoadWithPartialName karena membuat pemuatan assembly menjadi mudah, pertimbangkan bahwa apabila aplikasi Anda mengalami kegagalan dengan pesan kesalahan yang mengidentifikasi assembly yang hilang, kemungkinan akan memberikan pengalaman pengguna yang lebih baik dibandingkan secara otomatis menggunakan versi assembly yang tidak diketahui, yang dapat menyebabkan perilaku tidak terduga dan celah keamanan.
Hindari Memuat Rakitan ke Dalam Beberapa Konteks
Memuat rakitan ke dalam beberapa konteks dapat menyebabkan masalah identitas jenis. Jika jenis yang sama dimuat dari rakitan yang sama ke dalam dua konteks yang berbeda, seolah-olah dua jenis yang berbeda dengan nama yang sama telah dimuat. Sebuah InvalidCastException dilemparkan jika Anda mencoba mengubah satu jenis ke jenis lain, dengan pesan membingungkan bahwa jenis MyType
tersebut tidak dapat diubah ke tipe MyType
.
Misalnya, antarmuka ICommunicate
dideklarasikan dalam rakitan bernama Utility
, yang direferensikan oleh program Anda dan juga oleh rakitan lain yang dimuat program Anda. Rakitan lain ini berisi jenis yang mengimplementasikan ICommunicate
antarmuka, memungkinkan program Anda untuk menggunakannya.
Sekarang pertimbangkan apa yang terjadi ketika program Anda dijalankan. Rakitan yang direferensikan oleh program Anda dimuat ke dalam konteks beban default. Jika Anda memuat rakitan target dengan identitasnya, menggunakan metode Load, itu akan berada dalam konteks muatan default, dan begitu juga dengan dependensinya. Baik program Anda maupun assembly target akan menggunakan assembly yang sama Utility
.
Namun, andaikan Anda memuat assembly target dengan jalur berkasnya, menggunakan metode LoadFile. Rakitan dimuat tanpa konteks apa pun, sehingga dependensinya tidak dimuat secara otomatis. Anda mungkin memiliki handler untuk peristiwa AppDomain.AssemblyResolve yang menyediakan dependensi, dan mungkin memuat assembly Utility
tanpa konteks dengan menggunakan metode LoadFile. Sekarang, ketika Anda membuat instans dari tipe yang termasuk dalam target rakitan dan mencoba menetapkannya ke variabel tipe ICommunicate
, maka InvalidCastException terlempar karena runtime menganggap antarmuka ICommunicate
pada dua salinan rakitan Utility
sebagai tipe yang berbeda.
Ada banyak skenario lain di mana rakitan dapat dimuat ke dalam beberapa konteks. Pendekatan terbaik adalah menghindari konflik dengan merelokasi rakitan target di jalur aplikasi Anda dan menggunakan Load metode dengan nama tampilan lengkap. Rakitan kemudian dimuat ke dalam konteks beban default, dan kedua rakitan menggunakan rakitan yang sama Utility
.
Jika rakitan target harus tetap berada di luar jalur aplikasi Anda, Anda dapat menggunakan metode LoadFrom untuk memuatnya ke dalam konteks "load-from". Jika rakitan target dikompilasi dengan referensi ke assembly Utility
aplikasi Anda, maka rakitan tersebut akan menggunakan Utility
yang telah dimuat oleh aplikasi Anda dalam konteks pemuatan default. Harap dicatat bahwa masalah dapat terjadi jika rakitan target memiliki ketergantungan pada salinan rakitan Utility
yang terletak di luar jalur aplikasi Anda. Jika rakitan tersebut dimuat ke dalam konteks load-from sebelum aplikasi Anda memuat rakitan Utility
, proses pemuatan aplikasi Anda akan gagal.
Bagian Pertimbangkan Beralih ke Konteks Beban Default membahas alternatif untuk menggunakan beban jalur file seperti LoadFile dan LoadFrom.
Hindari Memuat Beberapa Versi Rakitan ke dalam Konteks yang Sama
Memuat beberapa versi rakitan ke dalam satu konteks beban dapat menyebabkan masalah identitas jenis. Jika tipe yang sama dimuat dari dua versi rakitan yang sama, ini seperti dua tipe berbeda dengan nama yang sama telah dimuat. Sebuah InvalidCastException dilemparkan jika Anda mencoba mengubah satu jenis ke jenis lain, dengan pesan membingungkan bahwa jenis MyType
tersebut tidak dapat diubah ke tipe MyType
.
Misalnya, program Anda mungkin memuat satu versi Utility
rakitan secara langsung, dan kemudian mungkin memuat rakitan lain yang memuat versi Utility
rakitan yang berbeda. Atau kesalahan pengodean dapat menyebabkan dua jalur kode yang berbeda dalam aplikasi Anda memuat versi rakitan yang berbeda.
Dalam konteks beban default, masalah ini dapat terjadi saat Anda menggunakan Assembly.Load metode dan menentukan nama tampilan rakitan lengkap yang menyertakan nomor versi yang berbeda. Untuk rakitan yang dimuat tanpa konteks, masalah dapat disebabkan oleh penggunaan Assembly.LoadFile metode untuk memuat rakitan yang sama dari jalur yang berbeda. Runtime menganggap dua assembly yang dimuat dari path yang berbeda sebagai assembly yang berbeda, bahkan jika identitasnya sama.
Selain masalah identitas jenis, berbagai versi rakitan dapat menyebabkan MissingMethodException jika jenis yang dimuat dari satu versi rakitan diteruskan ke kode yang mengharapkan jenis tersebut dari versi yang berbeda. Misalnya, kode mungkin mengharapkan metode yang ditambahkan ke versi yang lebih baru.
Kesalahan yang lebih samar dapat terjadi jika perilaku tipe berubah antara versi. Misalnya, metode mungkin melemparkan pengecualian tak terduga atau mengembalikan nilai yang tidak terduga.
Tinjau kode Anda dengan hati-hati untuk memastikan bahwa hanya satu versi rakitan yang dimuat. Anda dapat menggunakan AppDomain.GetAssemblies metode untuk menentukan rakitan mana yang dimuat pada waktu tertentu.
Pertimbangkan untuk beralih ke konteks muatan default
Periksa pola pemuatan dan penyebaran aplikasi Anda. Dapatkah Anda menghapus assembly yang dimuat dari array byte? Dapatkah Anda memindahkan komponen ke jalur pengujian? Jika rakitan terletak di cache perakitan global atau di jalur pemeriksaan domain aplikasi (yaitu, ApplicationBase dan PrivateBinPath), Anda dapat memuat rakitan berdasarkan identitasnya.
Jika tidak dimungkinkan untuk menempatkan semua rakitan Anda di jalur pemeriksaan, pertimbangkan alternatif seperti menggunakan model add-in .NET Framework, menempatkan rakitan ke dalam cache perakitan global, atau membuat domain aplikasi.
Pertimbangkan Untuk Menggunakan Model Add-In .NET Framework
Jika Anda menggunakan konteks "load-from" untuk menerapkan add-in, yang biasanya tidak diinstal di dasar aplikasi, gunakan model add-in .NET Framework. Model ini menyediakan isolasi di domain aplikasi atau tingkat proses, tanpa mengharuskan Anda mengelola domain aplikasi sendiri. Untuk informasi tentang model add-in, lihat Add-in dan Ekstensibilitas.
Pertimbangkan Menggunakan Cache Majelis Global
Simpan rakitan di cache perakitan global untuk mendapatkan manfaat jalur perakitan yang dibagi pakai yang berada di luar dasar aplikasi, tanpa kehilangan keuntungan dari konteks default atau menghadapi kekurangan konteks lainnya.
Pertimbangkan Untuk Menggunakan Domain Aplikasi
Jika Anda menentukan bahwa beberapa rakitan Anda tidak dapat disebarkan di jalur pemeriksaan aplikasi, pertimbangkan untuk membuat domain aplikasi baru untuk rakitan tersebut. AppDomainSetup Gunakan untuk membuat domain aplikasi baru, dan gunakan AppDomainSetup.ApplicationBase properti untuk menentukan jalur yang berisi rakitan yang ingin Anda muat. Jika Anda memiliki beberapa direktori untuk diprobesikan, Anda dapat mengatur ApplicationBase ke direktori akar dan menggunakan AppDomainSetup.PrivateBinPath properti untuk mengidentifikasi subdirektori untuk diprobesikan. Atau, Anda dapat membuat beberapa domain aplikasi dan mengatur ApplicationBase dari setiap domain aplikasi ke path yang sesuai untuk assembly-nya.
Perhatikan bahwa Anda dapat menggunakan Assembly.LoadFrom metode untuk memuat rakitan ini. Karena sekarang berada di jalur pencarian, mereka akan dimuat ke dalam konteks muatan default alih-alih konteks muat-dari. Namun, kami sarankan Anda beralih ke Assembly.Load metode dan menyediakan nama tampilan rakitan penuh untuk memastikan bahwa versi yang benar selalu digunakan.