Bagikan melalui


Praktik Terbaik untuk Pemuatan Rakitan

Catatan

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 identitas jenis yang dapat menyebabkan InvalidCastException, MissingMethodException, dan kesalahan lainnya. Artikel tersebut membahas rekomendasi berikut:

Rekomendasi pertama, memahami kelebihan dan kekurangan konteks pemuatan, memberikan informasi latar belakang untuk rekomendasi lainnya, karena semuanya bergantung pada pengetahuan tentang konteks pemuatan.

Pahami Kelebihan dan Kekurangan Konteks Beban

Dalam domain aplikasi, rakitan dapat dimuat ke salah satu dari tiga konteks, atau dapat dimuat tanpa konteks:

  • Konteks beban default berisi rakitan yang ditemukan dengan memeriksa singgahan perakitan global, penyimpanan rakitan host jika waktu proses dihosting (misalnya, di Microsoft SQL Server), dan ApplicationBase dan PrivateBinPath dari domain aplikasi. Sebagian besar kelebihan beban metode Load memuat rakitan ke dalam konteks ini.

  • Konteks load-from berisi rakitan yang dimuat dari lokasi yang tidak dicari oleh loader. Misalnya, add-in mungkin dipasang di direktori yang tidak berada di bawah jalur aplikasi. Assembly.LoadFrom, AppDomain.CreateInstanceFrom, dan AppDomain.ExecuteAssembly adalah contoh metode yang dimuat menurut jalur.

  • Konteks refleksi saja berisi rakitan yang dimuat dengan metode ReflectionOnlyLoad dan ReflectionOnlyLoadFrom. Kode dalam konteks ini tidak dapat dieksekusi, jadi tidak dibahas di sini. Untuk informasi lebih lanjut, lihat Cara: Memuat Rakitan ke dalam Konteks Hanya-Refleksi.

  • Jika Anda membuat rakitan dinamis sementara dengan menggunakan pancaran pantulan, rakitan tidak dalam konteks apa pun. Selain itu, sebagian besar rakitan yang dimuat dengan menggunakan metode LoadFile dimuat tanpa konteks, dan rakitan yang dimuat dari larik byte dimuat tanpa konteks kecuali identitasnya (setelah kebijakan diterapkan) menetapkan bahwa mereka berada di singgahan perakitan global.

Konteks eksekusi memiliki kelebihan dan kekurangan, seperti yang dibahas dalam bagian berikut.

Konteks Muat Default

Saat 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. Memuat dengan identitas rakitan meningkatkan stabilitas aplikasi dengan memastikan bahwa versi rakitan yang tidak dikenal tidak digunakan (lihat bagian Menghindari Pengikatan pada Nama Rakitan Parsial).

Menggunakan konteks beban default memiliki kelemahan sebagai berikut:

  • Dependensi yang dimuat ke dalam konteks lain tidak tersedia.

  • Anda tidak dapat memuat rakitan dari lokasi di luar jalur pemeriksaan ke dalam konteks beban default.

Konteks Load-From

Konteks load-from memungkinkan Anda memuat rakitan dari jalur yang tidak berada di bawah jalur aplikasi, dan oleh karena itu tidak disertakan dalam pemeriksaan. Ini memungkinkan dependensi untuk ditempatkan dan dimuat dari jalur itu, karena informasi jalur dipertahankan oleh konteksnya. Selain itu, rakitan dalam konteks ini dapat menggunakan dependensi yang dimuat ke dalam konteks beban default.

Memuat rakitan dengan menggunakan metode Assembly.LoadFrom, atau salah satu metode lain yang memuat menurut jalur, memiliki kelemahan sebagai berikut:

  • Jika rakitan dengan identitas yang sama sudah dimuat dalam konteks load-from, LoadFrom mengembalikan rakitan yang dimuat meskipun jalur yang berbeda telah ditentukan.

  • Jika rakitan dimuat dengan LoadFrom, dan kemudian rakitan dalam konteks beban default mencoba memuat rakitan yang sama dengan nama tampilan, upaya memuat gagal. Hal 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 tuntutan FileIOPermissionAccess.Read dan FileIOPermissionAccess.PathDiscovery, atau WebPermission, pada jalur yang ditentukan.

  • Jika gambar asli ada untuk rakitan, itu tidak digunakan.

  • Rakitan tidak dapat dimuat sebagai domain-netral.

  • Di .NET Framework versi 1.0 dan 1.1, kebijakan tidak diterapkan.

Tidak Ada Konteks

Memuat tanpa konteks adalah satu-satunya pilihan untuk rakitan sementara yang dihasilkan dengan pancaran pantulan. Memuat tanpa konteks adalah satu-satunya cara untuk memuat beberapa rakitan yang memiliki identitas yang sama ke dalam satu domain aplikasi. Biaya pemeriksaan dihindari.

Rakitan yang dimuat dari larik byte dimuat tanpa konteks kecuali identitas rakitan, yang ditetapkan saat kebijakan diterapkan, cocok dengan identitas rakitan di singgahan perakitan global; dalam hal ini, rakitan dimuat dari singgahan perakitan global.

Memuat rakitan tanpa konteks memiliki kelemahan sebagai berikut:

  • Rakitan lain tidak dapat mengikat ke rakitan yang dimuat tanpa konteks, kecuali jika Anda menangani peristiwa AppDomain.AssemblyResolve.

  • Dependensi tidak dimuat secara otomatis. Anda dapat memuatnya terlebih dahulu tanpa konteks, memuatnya terlebih dahulu ke dalam konteks pemuatan default, atau memuatnya dengan menangani peristiwa AppDomain.AssemblyResolve.

  • Memuat beberapa rakitan dengan identitas yang sama tanpa konteks dapat menyebabkan masalah identitas jenis yang serupa dengan yang disebabkan pemuatan rakitan dengan identitas yang sama ke dalam beberapa konteks. Lihat Menghindari Memuat Rakitan ke Beberapa Konteks.

  • Jika gambar asli ada untuk rakitan, itu tidak digunakan.

  • Rakitan tidak dapat dimuat sebagai domain-netral.

  • Di .NET Framework versi 1.0 dan 1.1, kebijakan tidak diterapkan.

Menghindari Pengikatan pada Nama Rakitan Parsial

Pengikatan nama sebagian terjadi saat Anda menentukan hanya sebagian dari nama tampilan rakitan (FullName) saat Anda memuat rakitan. Misalnya, Anda dapat memanggil metode Assembly.Load hanya dengan nama sederhana dari rakitan, menghilangkan versi, budaya, dan token kunci umum. Atau Anda dapat memanggil metode Assembly.LoadWithPartialName, yang pertama-tama memanggil metode Assembly.Load dan, jika itu gagal menemukan rakitan, mencari singgahan perakitan global dan memuat versi rakitan terbaru yang tersedia.

Pengikatan nama sebagian dapat menyebabkan banyak masalah, termasuk berikut ini:

  • Metode Assembly.LoadWithPartialName mungkin memuat rakitan berbeda dengan nama sederhana yang sama. Misalnya, dua aplikasi mungkin menginstal dua rakitan yang sangat berbeda yang keduanya memiliki nama sederhana GraphicsLibrary ke dalam singgahan perakitan global.

  • Rakitan yang benar-benar dimuat mungkin tidak kompatibel dengan versi sebelumnya. Misalnya, tidak menentukan versi dapat mengakibatkan pemuatan versi yang jauh lebih baru daripada versi yang awalnya ditulis untuk digunakan program Anda. Perubahan pada versi yang lebih baru dapat menyebabkan kesalahan pada aplikasi Anda.

  • Rakitan yang benar-benar dimuat mungkin tidak kompatibel dengan versi setelahnya. Misalnya, Anda mungkin telah membuat dan menguji aplikasi Anda dengan versi rakitan terbaru, tetapi pengikatan sebagian mungkin memuat versi yang jauh lebih lama yang tidak memiliki fitur yang digunakan aplikasi Anda.

  • Menginstal aplikasi baru dapat merusak aplikasi yang sudah ada. Aplikasi yang menggunakan metode LoadWithPartialName dapat rusak dengan memasang versi rakitan bersama yang lebih baru dan tidak kompatibel.

  • Pemuatan ketergantungan yang tidak terduga dapat terjadi. Jika Anda memuat dua rakitan yang berbagi ketergantungan, memuatnya dengan pengikatan parsial dapat menghasilkan satu rakitan menggunakan komponen yang tidak dibuat atau diuji.

Karena masalah yang dapat ditimbulkannya, metode LoadWithPartialName telah ditandai usang. Sebaiknya gunakan metode Assembly.Load sebagai gantinya, dan tentukan nama tampilan rakitan lengkap. Lihat Memahami Keuntungan dan Kerugian Konteks Pemuatan dan Pertimbangkan Beralih ke Konteks Pemuatan Default.

Jika Anda ingin menggunakan metode LoadWithPartialName karena memudahkan pemuatan perakitan, pertimbangkan bahwa aplikasi Anda gagal dengan pesan kesalahan yang mengidentifikasi perakitan yang hilang kemungkinan akan memberikan pengalaman pengguna yang lebih baik daripada secara otomatis menggunakan versi perakitan yang tidak diketahui, yang mungkin menyebabkan perilaku tak terduga dan lubang keamanan.

Menghindari Memuat Rakitan ke 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 berbeda dengan nama yang sama telah dimuat. InvalidCastException ditampilkan jika Anda mencoba mentransmisikan satu jenis ke jenis lainnya, dengan pesan yang membingungkan bahwa jenis MyType tidak dapat ditransmisikan untuk diketik MyType.

Misalnya, anggaplah antarmuka ICommunicate dideklarasikan dalam rakitan bernama Utility, yang direferensikan oleh program Anda dan juga oleh rakitan lain yang dimuat oleh program Anda. Rakitan lain ini berisi jenis yang mengimplementasikan antarmuka ICommunicate, yang 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, rakitan tersebut akan berada dalam konteks beban default, dan begitu juga dependensinya. Program Anda dan rakitan target akan menggunakan rakitan Utility yang sama.

Namun, misalkan Anda memuat rakitan target dengan jalur filenya, menggunakan metode LoadFile. Rakitan dimuat tanpa konteks apa pun, sehingga dependensinya tidak dimuat secara otomatis. Anda mungkin memiliki penangan untuk peristiwa AppDomain.AssemblyResolve untuk memasok dependensi, dan mungkin memuat rakitan Utility tanpa konteks dengan menggunakan metode LoadFile. Sekarang ketika Anda membuat instans dari tipe yang terdapat dalam rakitan target dan mencoba menetapkannya ke variabel jenis ICommunicate, InvalidCastException ditampilkan karena runtime bahasa umum mempertimbangkan antarmuka ICommunicate dalam dua salinan perakitan Utility menjadi jenis yang berbeda.

Ada banyak skenario lain di mana rakitan dapat dimuat ke dalam berbagai konteks. Pendekatan terbaik adalah menghindari konflik dengan merelokasi rakitan target di jalur aplikasi Anda dan menggunakan metode Load dengan nama tampilan lengkap. Rakitan kemudian dimuat ke dalam konteks beban default, dan kedua rakitan menggunakan rakitan Utility yang sama.

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 rakitan Utility aplikasi Anda, ini akan menggunakan rakitan Utility yang telah dimuat aplikasi Anda ke dalam konteks pemuatan default. Perhatikan bahwa masalah dapat terjadi jika rakitan target memiliki dependensi pada salinan rakitan Utility yang terletak di luar jalur aplikasi Anda. Jika rakitan itu dimuat ke dalam konteks load-from sebelum aplikasi Anda memuat rakitan Utility, pemuatan aplikasi Anda akan gagal.

Bagian Pertimbangkan Beralih ke Konteks Pemuatan Default membahas alternatif untuk menggunakan pemuatan jalur file seperti LoadFile dan LoadFrom.

Menghindari Memuat Beberapa Versi Rakitan ke dalam Konteks yang Sama

Memuat beberapa versi rakitan ke dalam satu konteks beban dapat menyebabkan masalah identitas jenis. Jika jenis yang sama dimuat dari dua versi rakitan yang sama, seolah-olah dua jenis berbeda dengan nama yang sama telah dimuat. InvalidCastException ditampilkan jika Anda mencoba mentransmisikan satu jenis ke jenis lainnya, dengan pesan yang membingungkan bahwa jenis MyType tidak dapat ditransmisikan untuk diketik MyType.

Misalnya, program Anda mungkin memuat satu versi rakitan Utility secara langsung, dan kemudian mungkin memuat rakitan lain yang memuat versi rakitan Utility yang berbeda. Atau kesalahan pengkodean dapat menyebabkan dua jalur kode berbeda di aplikasi Anda memuat versi rakitan yang berbeda.

Dalam konteks pemuatan default, masalah ini dapat terjadi saat Anda menggunakan metode Assembly.Load dan menentukan nama tampilan rakitan lengkap yang menyertakan nomor versi berbeda. Untuk rakitan yang dimuat tanpa konteks, masalah dapat disebabkan oleh penggunaan metode Assembly.LoadFile untuk memuat rakitan yang sama dari jalur yang berbeda. Runtime bahasa umum menganggap dua rakitan yang dimuat dari jalur berbeda sebagai rakitan yang berbeda, meskipun identitasnya sama.

Selain masalah identitas jenis, beberapa versi rakitan dapat menyebabkan MissingMethodException jika jenis yang dimuat dari satu versi rakitan diteruskan ke kode yang memperkirakan jenis itu dari versi yang berbeda. Misalnya, kode mungkin memperkirakan metode yang ditambahkan ke versi yang lebih baru.

Kesalahan yang lebih halus dapat terjadi jika perilaku tipe berubah di antara versi. Misalnya, suatu metode mungkin menampilkan pengecualian yang tidak terduga atau mengembalikan nilai yang tidak terduga.

Tinjau kode Anda dengan teliti untuk memastikan bahwa hanya satu versi rakitan yang dimuat. Anda dapat menggunakan metode AppDomain.GetAssemblies untuk menentukan rakitan mana yang dimuat pada waktu tertentu.

Pertimbangkan Beralih ke Konteks Pemuatan Default

Periksa pola pemuatan dan penerapan perakitan aplikasi Anda. Bisakah Anda menghilangkan rakitan yang dimuat dari larik byte? Bisakah Anda memindahkan rakitan ke jalur probing? Jika rakitan terletak di singgahan perakitan global atau di jalur penyelidikan domain aplikasi (yaitu, ApplicationBase dan PrivateBinPath), Anda dapat memuat rakitan dengan identitasnya.

Jika tidak mungkin untuk meletakkan semua rakitan Anda di jalur pemeriksaan, pertimbangkan alternatif seperti menggunakan model add-in .NET Framework, menempatkan rakitan ke dalam singgahan perakitan global, atau membuat domain aplikasi.

Pertimbangkan Menggunakan Model Add-In .NET Framework

Jika Anda menggunakan konteks load-from untuk menerapkan add-in, yang biasanya tidak diinstal di basis 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 Singgahan Perakitan Global

Tempatkan rakitan di singgahan perakitan global untuk mendapatkan manfaat dari jalur rakitan bersama yang berada di luar basis aplikasi, tanpa kehilangan keuntungan dari konteks beban default atau mengambil kerugian dari konteks lain.

Pertimbangkan Menggunakan Domain Aplikasi

Jika Anda menentukan bahwa beberapa rakitan tidak dapat digunakan di jalur pemeriksaan aplikasi, pertimbangkan untuk membuat domain aplikasi baru untuk rakitan tersebut. Gunakan AppDomainSetup untuk membuat domain aplikasi baru, dan gunakan properti AppDomainSetup.ApplicationBase untuk menentukan jalur yang berisi rakitan yang ingin Anda muat. Jika Anda memiliki beberapa direktori untuk diselidiki, Anda dapat mengatur ApplicationBase ke direktori akar dan menggunakan properti AppDomainSetup.PrivateBinPath untuk mengidentifikasi subdirektori yang akan diselidiki. Atau, Anda dapat membuat beberapa domain aplikasi dan mengatur ApplicationBase setiap domain aplikasi ke jalur yang sesuai untuk rakitannya.

Perhatikan bahwa Anda dapat menggunakan metode Assembly.LoadFrom untuk memuat rakitan ini. Karena sekarang berada di jalur probing, ini akan dimuat ke dalam konteks beban default bukannya konteks load-from. Namun, kami menyarankan Anda beralih ke metode Assembly.Load dan memberikan nama tampilan rakitan lengkap untuk memastikan bahwa versi yang benar selalu digunakan.

Lihat juga