Saya suka instantiating klien layanan WCF saya dalam satu using
blok karena cukup banyak cara standar untuk menggunakan sumber daya yang mengimplementasikan IDisposable
:
using (var client = new SomeWCFServiceClient())
{
//Do something with the client
}
Tapi, seperti yang disebutkan dalam artikel MSDN ini , membungkus klien WCF dalam sebuah using
blok bisa menutupi kesalahan yang mengakibatkan klien dibiarkan dalam keadaan rusak (seperti waktu habis atau masalah komunikasi). Singkatnya, ketika Buang () dipanggil, metode Tutup () klien akan menyala, tetapi melempar kesalahan karena itu dalam kondisi rusak. Pengecualian asli kemudian ditutup oleh pengecualian kedua. Tidak baik.
Solusi yang disarankan dalam artikel MSDN adalah untuk sepenuhnya menghindari menggunakan using
blok, dan sebagai gantinya instantiate klien Anda dan menggunakannya sesuatu seperti ini:
try
{
...
client.Close();
}
catch (CommunicationException e)
{
...
client.Abort();
}
catch (TimeoutException e)
{
...
client.Abort();
}
catch (Exception e)
{
...
client.Abort();
throw;
}
Dibandingkan dengan using
blok, saya pikir itu jelek. Dan banyak kode untuk ditulis setiap kali Anda membutuhkan klien.
Untungnya, saya menemukan beberapa solusi lain, seperti yang ini di IServiceOriented. Anda mulai dengan:
public delegate void UseServiceDelegate<T>(T proxy);
public static class Service<T>
{
public static ChannelFactory<T> _channelFactory = new ChannelFactory<T>("");
public static void Use(UseServiceDelegate<T> codeBlock)
{
IClientChannel proxy = (IClientChannel)_channelFactory.CreateChannel();
bool success = false;
try
{
codeBlock((T)proxy);
proxy.Close();
success = true;
}
finally
{
if (!success)
{
proxy.Abort();
}
}
}
}
Yang kemudian memungkinkan:
Service<IOrderService>.Use(orderService =>
{
orderService.PlaceOrder(request);
});
Itu tidak buruk, tetapi saya tidak berpikir itu sebagai ekspresif dan mudah dimengerti sebagai using
blok.
Solusi yang saat ini saya coba gunakan adalah yang pertama saya baca di blog.davidbarret.net . Pada dasarnya Anda mengganti metode klien di Dispose()
mana pun Anda menggunakannya. Sesuatu seperti:
public partial class SomeWCFServiceClient : IDisposable
{
void IDisposable.Dispose()
{
if (this.State == CommunicationState.Faulted)
{
this.Abort();
}
else
{
this.Close();
}
}
}
Ini tampaknya dapat memungkinkan using
blok lagi tanpa bahaya menutupi pengecualian kondisi yang salah.
Jadi, apakah ada gotchas lain yang harus saya perhatikan untuk menggunakan solusi ini? Adakah yang datang dengan sesuatu yang lebih baik?
Action<T>
sebagai gantinyaUseServiceDelegate<T>
. minor.Service<T>
karena mempersulit pengujian unit (seperti kebanyakan hal statis lakukan). Saya lebih suka itu non-statis sehingga dapat disuntikkan ke kelas yang menggunakannya.Jawaban:
Sebenarnya, meskipun saya ngeblog (lihat jawaban Luke ), saya pikir ini lebih baik daripada bungkus IDisposable saya. Kode khas:
(edit per komentar)
Sejak
Use
pengembalian batal, cara termudah untuk menangani nilai kembali adalah melalui variabel yang ditangkap:sumber
public static TResult Use<TResult>(Func<T, TResult> codeBlock) { ... }
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
danhttps://devzone.channeladam.com/articles/2014/09/how-to-easily-call-wcf-service-properly/
danhttp://dzimchuk.net/post/wcf-error-helpers
https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
Diberi pilihan antara solusi yang disarankan oleh IServiceOriented.com dan solusi yang disarankan oleh blog David Barret , saya lebih suka kesederhanaan yang ditawarkan dengan mengabaikan metode Dispose () klien. Ini memungkinkan saya untuk terus menggunakan pernyataan using () seperti yang diharapkan orang dengan objek sekali pakai. Namun, seperti yang ditunjukkan oleh @Brian, solusi ini berisi kondisi balapan di mana Negara mungkin tidak disalahkan ketika dicentang tetapi bisa pada saat Close () dipanggil, dalam hal ini CommunicationException masih terjadi.
Jadi, untuk menyiasati ini, saya telah menggunakan solusi yang memadukan yang terbaik dari kedua dunia.
sumber
success
flag? Mengapa tidaktry { Close(); } catch { Abort(); throw; }
?Close(); success = true;
? Saya tidak ingin ada pengecualian yang dilemparkan jika saya berhasil membatalkannya di blok terakhir. Saya hanya ingin pengecualian dilemparkan jika Abort () gagal dalam kasus itu. Dengan cara ini, coba / tangkap akan menyembunyikan pengecualian kondisi lomba potensial dan masih memungkinkan Anda untuk membatalkan () koneksi di blok akhirnya.Saya menulis fungsi tingkat tinggi untuk membuatnya berfungsi dengan benar. Kami telah menggunakan ini di beberapa proyek dan tampaknya bekerja dengan baik. Inilah yang seharusnya dilakukan sejak awal, tanpa paradigma "menggunakan" atau seterusnya.
Anda dapat melakukan panggilan seperti ini:
Ini cukup banyak seperti yang Anda miliki dalam contoh Anda. Dalam beberapa proyek, kami menulis metode pembantu yang sangat diketik, jadi kami akhirnya menulis hal-hal seperti "Wcf.UseFooService (f => f ...)".
Saya merasa cukup elegan, semua hal dipertimbangkan. Apakah ada masalah khusus yang Anda temui?
Ini memungkinkan fitur bagus lainnya untuk dicolokkan. Misalnya, di satu situs, situs mengautentikasi ke layanan atas nama pengguna yang masuk. (Situs tidak memiliki kredensial dengan sendirinya.) Dengan menulis pembantu metode "UseService" kami sendiri, kami dapat mengonfigurasi pabrik saluran seperti yang kami inginkan, dll. Kami juga tidak terikat untuk menggunakan proxy yang dihasilkan - antarmuka apa pun akan melakukan .
sumber
GetCachedFactory
metode?Ini adalah cara yang disarankan Microsoft untuk menangani panggilan klien WCF:
Untuk detail lebih lanjut, lihat: Pengecualian yang Diharapkan
Informasi tambahan Begitu banyak orang tampaknya mengajukan pertanyaan ini di WCF sehingga Microsoft bahkan membuat sampel khusus untuk menunjukkan bagaimana menangani pengecualian:
c: \ WF_WCF_Samples \ WCF \ Basic \ Client \ ExpectedExceptions \ CS \ client
Unduh sampel: C # atau VB
Mempertimbangkan bahwa ada begitu banyak masalah yang melibatkan pernyataan menggunakan , (dipanaskan?) Diskusi internal dan utas tentang masalah ini, saya tidak akan membuang waktu saya mencoba menjadi koboi kode dan menemukan cara yang lebih bersih. Saya hanya akan menyedotnya, dan mengimplementasikan klien WCF cara verbose (belum tepercaya) ini untuk aplikasi server saya.
Kegagalan tambahan opsional untuk ditangkap
Banyak pengecualian berasal dari
CommunicationException
dan saya tidak berpikir sebagian besar pengecualian harus diulang. Saya menelusuri setiap pengecualian pada MSDN dan menemukan daftar pendek pengecualian yang dapat dicoba lagi (selain diTimeOutException
atas). Beri tahu saya jika saya melewatkan pengecualian yang harus diulang.Memang, ini adalah sedikit kode biasa untuk ditulis. Saat ini saya lebih suka jawaban ini , dan tidak melihat "peretasan" dalam kode yang dapat menyebabkan masalah di jalan.
sumber
"Hope this code wasn't important, because it might not happen."
masih dieksekusi ...Saya akhirnya menemukan beberapa langkah kuat menuju solusi bersih untuk masalah ini.
Codeplex memiliki proyek yang disebut Exception Handling WCF Proxy Generator . Ini pada dasarnya menginstal alat kustom baru ke Visual Studio 2008, kemudian gunakan alat ini untuk menghasilkan proksi layanan baru (Tambahkan referensi layanan) . Ini memiliki beberapa fungsionalitas yang bagus untuk menangani saluran yang rusak, batas waktu dan pembuangan yang aman. Ada video luar biasa di sini yang disebut ExceptionHandlingProxyWrapper yang menjelaskan cara kerjanya.
Anda dapat menggunakan
Using
pernyataan itu lagi dengan aman , dan jika salurannya salah pada permintaan apa pun (TimeoutException atau CommunicationException), Pembungkus akan menginisialisasi ulang saluran yang rusak dan mencoba lagi kueri. Jika itu gagal maka akan memanggilAbort()
perintah dan membuang proxy dan rethrow Pengecualian. Jika layanan melemparFaultException
kode itu akan berhenti mengeksekusi, dan proksi akan dibatalkan dengan aman melemparkan pengecualian yang benar seperti yang diharapkan.sumber
Berdasarkan jawaban Marc Gravell, MichaelGG, dan Matt Davis, pengembang kami membuat yang berikut:
Contoh penggunaan:
Itu sedekat mungkin dengan sintaks "menggunakan", Anda tidak harus mengembalikan nilai dummy saat memanggil metode void, dan Anda dapat membuat beberapa panggilan ke layanan (dan mengembalikan beberapa nilai) tanpa harus menggunakan tupel.
Selain itu, Anda dapat menggunakan ini dengan
ClientBase<T>
keturunan daripada ChannelFactory jika diinginkan.Metode ekstensi terbuka jika pengembang ingin membuang proxy / saluran secara manual.
sumber
DisposeSafely
pribadi merupakan pilihan, dan akan menghindari kebingungan. Mungkin ada kasus penggunaan di mana seseorang ingin memanggilnya secara langsung, tetapi saya tidak dapat menemukan satu kasus begitu saja.https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
@Marc Gravell
Bukankah boleh menggunakan ini:
Atau, hal yang sama
(Func<T, TResult>)
jika terjadiService<IOrderService>.Use
Ini akan membuat variabel pengembalian lebih mudah.
sumber
Apa ini?
Ini adalah versi CW dari jawaban yang diterima tetapi dengan (apa yang saya anggap lengkap) termasuk penanganan Perkecualian.
Jawaban yang diterima merujuk situs web ini yang tidak lagi ada . Untuk menghemat masalah Anda, saya menyertakan bagian yang paling relevan di sini. Selain itu, saya memodifikasinya sedikit untuk memasukkan penanganan retry pengecualian untuk menangani timeout jaringan yang sial itu.
Penggunaan Klien WCF Sederhana
Setelah Anda menghasilkan proxy sisi klien Anda, ini yang Anda butuhkan untuk mengimplementasikannya.
ServiceDelegate.cs
Tambahkan file ini ke solusi Anda. Tidak diperlukan perubahan pada file ini, kecuali jika Anda ingin mengubah jumlah coba lagi atau pengecualian apa yang ingin Anda tangani.
PS: Saya membuat posting ini sebagai wiki komunitas. Saya tidak akan mengumpulkan "poin" dari jawaban ini, tetapi saya lebih suka Anda meningkatkannya jika Anda setuju dengan implementasi, atau mengeditnya untuk membuatnya lebih baik.
sumber
success == false
ke final jika pernyataanDi bawah ini adalah versi sumber yang disempurnakan dari pertanyaan dan diperluas untuk men -cache beberapa pabrik saluran dan berupaya mencari titik akhir dalam file konfigurasi dengan nama kontrak.
Menggunakan .NET 4 (khusus: contravariance, LINQ,
var
):sumber
UseServiceDelegate<T>
bukanAction<T>
?Action<T>
bekerja dengan baik.Pembungkus seperti ini berfungsi:
Itu akan memungkinkan Anda untuk menulis kode seperti:
Pembungkus tentu saja dapat menangkap lebih banyak pengecualian jika itu diperlukan, tetapi prinsipnya tetap sama.
sumber
Dispose
IChannel, ia bisa melempar pengecualian jika salurannya dalam kondisi rusak, ini merupakan masalah karena Microsoft menetapkan bahwaDispose
seharusnya tidak pernah melempar. Jadi yang dilakukan oleh kode di atas adalah menangani case ketikaClose
melempar pengecualian. JikaAbort
melempar itu mungkin ada sesuatu yang salah serius. Saya menulis posting blog tentang hal itu Desember lalu: blog.tomasjansson.com/2010/12/disposible-wcf-client-wrapperSaya menggunakan proksi dinamis Castle untuk menyelesaikan masalah Buang (), dan juga menerapkan penyegaran otomatis saluran saat berada dalam keadaan tidak dapat digunakan. Untuk menggunakan ini, Anda harus membuat antarmuka baru yang mewarisi kontrak layanan dan IDisposable Anda. Proxy dinamis mengimplementasikan antarmuka ini dan membungkus saluran WCF:
Saya suka ini karena Anda dapat menyuntikkan layanan WCF tanpa konsumen perlu khawatir tentang rincian WCF. Dan tidak ada tambahan cruft seperti solusi lainnya.
Lihatlah kodenya, sebenarnya cukup sederhana: WCF Dynamic Proxy
sumber
Gunakan metode ekstensi:
sumber
Jika Anda tidak memerlukan IoC atau menggunakan klien yang di-autogenerasi (Referensi Layanan), maka Anda dapat menggunakan pembungkus sederhana untuk mengelola penutupan dan membiarkan GC mengambil basis klien ketika berada dalam keadaan aman yang tidak akan membuang pengecualian. GC akan memanggil Buang dalam layanan serviceclient, dan ini akan memanggil
Close
. Karena sudah dibaca tertutup, tidak dapat menyebabkan kerusakan. Saya menggunakan ini tanpa masalah dalam kode produksi.Kemudian ketika Anda mengakses server, Anda membuat klien dan menggunakan
using
autodisconect:sumber
Ringkasan
Menggunakan teknik yang dijelaskan dalam jawaban ini orang dapat mengkonsumsi layanan WCF di blok menggunakan dengan sintaks berikut:
Anda tentu saja dapat menyesuaikan ini lebih jauh untuk mencapai model pemrograman yang lebih ringkas khusus untuk situasi Anda - tetapi intinya adalah bahwa kita dapat membuat implementasi
IMyService
reprenting saluran yang mengimplementasikan pola disposable yang benar.Detail
Semua jawaban yang diberikan sejauh ini mengatasi masalah untuk mengatasi "bug" dalam implementasi WCF Channel
IDisposable
. Jawaban yang tampaknya menawarkan model pemrograman yang paling ringkas (memungkinkan Anda untuk menggunakanusing
blok untuk membuang sumber daya yang tidak dikelola) adalah yang ini - di mana proxy dimodifikasi untuk diimplementasikanIDisposable
dengan implementasi bebas bug. Masalah dengan pendekatan ini adalah kemampuan pemeliharaan - kita harus mengimplementasikan kembali fungsi ini untuk proksi yang pernah kita gunakan. Pada variasi jawaban ini kita akan melihat bagaimana kita dapat menggunakan komposisi daripada pewarisan untuk membuat teknik ini generik.Percobaan pertama
Tampaknya ada berbagai implementasi untuk
IDisposable
implementasi, tetapi demi argumen kami akan menggunakan adaptasi yang digunakan oleh jawaban yang saat ini diterima .Berbekal kelas-kelas di atas sekarang kita bisa menulis
Ini memungkinkan kami untuk menggunakan layanan kami menggunakan
using
blok:Membuat ini generik
Yang kami lakukan sejauh ini adalah merumuskan kembali solusi Tomas . Apa yang mencegah kode ini menjadi generik adalah kenyataan bahwa
ProxyWrapper
kelas harus diimplementasikan kembali untuk setiap kontrak layanan yang kita inginkan. Kita sekarang akan melihat kelas yang memungkinkan kita untuk membuat tipe ini secara dinamis menggunakan IL:Dengan kelas pembantu baru kami, kami sekarang dapat menulis
Perhatikan bahwa Anda juga bisa menggunakan teknik yang sama (dengan sedikit modifikasi) untuk klien yang dihasilkan secara otomatis untuk
ClientBase<>
(alih-alih menggunakanChannelFactory<>
), atau jika Anda ingin menggunakan implementasi berbedaIDisposable
untuk menutup saluran Anda.sumber
Saya suka cara menutup koneksi ini:
sumber
Saya telah menulis kelas dasar sederhana yang menangani ini. Ini tersedia sebagai paket NuGet dan cukup mudah digunakan.
sumber
Jadi memungkinkan untuk menulis pernyataan pengembalian dengan baik:
sumber
Saya ingin menambahkan implementasi Layanan dari jawaban Marc Gravell untuk kasus menggunakan ServiceClient alih-alih ChannelFactory.
sumber
Bagi yang berminat, inilah terjemahan VB.NET dari jawaban yang diterima (di bawah). Saya telah memperbaikinya sedikit untuk singkatnya, menggabungkan beberapa tips oleh orang lain di utas ini.
Saya akui itu di luar topik untuk tag asal (C #), tetapi karena saya tidak dapat menemukan versi VB.NET dari solusi yang bagus ini, saya berasumsi bahwa orang lain juga akan mencari. Terjemahan Lambda bisa sedikit rumit, jadi saya ingin menyelamatkan seseorang dari masalah.
Perhatikan bahwa implementasi khusus ini menyediakan kemampuan untuk mengkonfigurasi
ServiceEndpoint
saat runtime.Kode:
Pemakaian:
sumber
Arsitektur sistem kami sering menggunakan Unity IOC kerangka kerja untuk membuat contoh dari ClientBase sehingga tidak ada cara yang pasti untuk menegakkan bahwa pengembang lain bahkan digunakan
using{}
blok. Untuk menjadikannya sebagai bukti semudah mungkin, saya membuat kelas khusus ini yang memperpanjang ClientBase, dan menangani penutupan saluran pada saat pembuangan, atau pada penyelesaian jika seseorang tidak secara eksplisit membuang contoh yang dibuat Unity.Ada juga hal-hal yang perlu dilakukan di konstruktor untuk mengatur saluran untuk kredensial khusus dan hal-hal lain, jadi itu juga ada di sini ...
Maka klien dapat dengan mudah:
Dan penelepon dapat melakukan semua ini:
sumber
Saya merujuk beberapa jawaban pada posting ini dan menyesuaikannya sesuai kebutuhan saya.
Saya ingin kemampuan untuk melakukan sesuatu dengan klien WCF sebelum menggunakannya jadi
DoSomethingWithClient()
metode.Inilah kelas pembantu:
Dan saya bisa menggunakannya sebagai:
sumber
Saya memiliki pembungkus sendiri untuk saluran yang mengimplementasikan Buang sebagai berikut:
Ini tampaknya bekerja dengan baik dan memungkinkan blok yang digunakan untuk digunakan.
sumber
Pembantu berikut memungkinkan untuk memanggil
void
dan metode yang tidak berlaku. Pemakaian:Kelas itu sendiri adalah:
sumber
Mengesampingkan Buang klien () tanpa perlu membuat kelas proxy berdasarkan ClientBase, juga tanpa perlu mengelola pembuatan saluran dan caching ! (Perhatikan bahwa WcfClient bukan kelas ABSTRAK dan didasarkan pada ClientBase)
sumber
Metode saya melakukan ini adalah untuk membuat kelas yang diwarisi yang secara eksplisit mengimplementasikan IDisposable. Ini berguna untuk orang-orang yang menggunakan gui untuk menambahkan referensi layanan (Tambah Referensi Layanan). Saya hanya membuang kelas ini di proyek yang membuat referensi layanan dan menggunakannya sebagai ganti klien default:
Catatan: Ini hanyalah implementasi sederhana dari buang, Anda dapat menerapkan logika buang yang lebih kompleks jika Anda mau.
Anda kemudian dapat mengganti semua panggilan yang dilakukan dengan klien layanan reguler dengan klien aman, seperti ini:
Saya suka solusi ini karena tidak mengharuskan saya untuk memiliki akses ke definisi Antarmuka dan saya dapat menggunakan
using
pernyataan seperti yang saya harapkan sambil membiarkan kode saya terlihat kurang lebih sama.Anda masih perlu menangani pengecualian yang dapat dilontarkan seperti ditunjukkan dalam komentar lain di utas ini.
sumber
Anda juga bisa menggunakan a
DynamicProxy
untuk memperluasDispose()
metode. Dengan cara ini Anda dapat melakukan sesuatu seperti:sumber