WCF क्लाइंट के लिए `ब्लॉक इश्यू का उपयोग करके सबसे अच्छा समाधान क्या है?


404

मुझे अपने WCF सेवा ग्राहकों को एक usingब्लॉक के भीतर इंस्टेंट करना पसंद है क्योंकि यह उन संसाधनों का उपयोग करने के लिए बहुत मानक तरीका है जो लागू होते हैं IDisposable:

using (var client = new SomeWCFServiceClient()) 
{
    //Do something with the client 
}

लेकिन, जैसा कि इस MSDN लेख में कहा गया है , एक usingब्लॉक में WCF क्लाइंट को लपेटने से कोई भी त्रुटि हो सकती है, जिसके परिणामस्वरूप क्लाइंट को एक दोषपूर्ण स्थिति (जैसे कि टाइमआउट या संचार समस्या) में छोड़ दिया जाता है। लंबी कहानी छोटी, जब डिस्पोज़ () कहा जाता है, क्लाइंट की क्लोज़ () विधि आग लग जाती है, लेकिन एक त्रुटि को फेंक देती है क्योंकि यह एक दोषपूर्ण स्थिति में है। मूल अपवाद को तब दूसरे अपवाद द्वारा नकाबपोश किया जाता है। अच्छा नही।

MSDN आलेख में सुझाया गया वर्कअराउंड usingब्लॉक का उपयोग करने से पूरी तरह से बचने के लिए है , और इसके बजाय अपने ग्राहकों को तुरंत और उन्हें इस तरह से उपयोग करने के लिए:

try
{
    ...
    client.Close();
}
catch (CommunicationException e)
{
    ...
    client.Abort();
}
catch (TimeoutException e)
{
    ...
    client.Abort();
}
catch (Exception e)
{
    ...
    client.Abort();
    throw;
}

usingब्लॉक की तुलना में , मुझे लगता है कि यह बदसूरत है। और आपको क्लाइंट की आवश्यकता होने पर हर बार लिखने के लिए बहुत सारे कोड।

सौभाग्य से, मुझे कुछ अन्य वर्कअराउंड्स मिले, जैसे कि इस पर IServiceOriented। आप इसके साथ शुरू करते हैं:

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(); 
            } 
        } 
     } 
} 

जो तब अनुमति देता है:

Service<IOrderService>.Use(orderService => 
{ 
    orderService.PlaceOrder(request); 
}); 

यह बुरा नहीं है, लेकिन मुझे नहीं लगता कि यह एक्सप्रेसिव के रूप में है और usingब्लॉक के रूप में आसानी से समझा जा सकता है ।

वर्तमान में मैं जिस वर्कअराउंड का उपयोग करने की कोशिश कर रहा हूं, वह मैंने पहले blog.davidbarret.net पर पढ़ा था । मूल रूप से आप ग्राहक की Dispose()विधि को ओवरराइड करते हैं जहाँ भी आप इसका उपयोग करते हैं। कुछ इस तरह:

public partial class SomeWCFServiceClient : IDisposable
{
    void IDisposable.Dispose() 
    {
        if (this.State == CommunicationState.Faulted) 
        {
            this.Abort();
        } 
        else 
        {
            this.Close();
        }
    }
}

यह usingएक दोषपूर्ण राज्य अपवाद को मास्क करने के खतरे के बिना ब्लॉक को फिर से अनुमति देने में सक्षम प्रतीत होता है।

तो, क्या इन वर्कअराउंड्स का उपयोग करने के लिए मुझे कोई अन्य गोचर देखना होगा? क्या किसी के साथ कुछ बेहतर हुआ है?


42
अंतिम एक (जो यह निरीक्षण करता है। यह एक दौड़ है); जब आप बूलियन की जांच करते हैं, तो यह दोषपूर्ण नहीं हो सकता है, लेकिन जब आप बंद () कॉल करते हैं तो यह दोषपूर्ण हो सकता है।
ब्रायन

15
आप राज्य पढ़ें; यह गलत नहीं है। इससे पहले कि आप बंद करें (), चैनल दोष। बंद () फेंकता है। खेल खत्म।
ब्रायन

4
समय गुजरता। यह समय की एक बहुत छोटी अवधि हो सकती है, लेकिन तकनीकी रूप से, चैनल की स्थिति की जाँच करने और इसे बंद करने के लिए कहने के बीच की समय अवधि में, चैनल की स्थिति बदल सकती है।
एरिक किंग

8
मैं Action<T>इसके बजाय का उपयोग करेंगे UseServiceDelegate<T>। नाबालिग।
hppyy

2
मैं वास्तव में इस स्थिर सहायक को पसंद नहीं करता हूं Service<T>क्योंकि यह इकाई परीक्षण को जटिल करता है (जैसा कि अधिकांश स्थिर चीजें करते हैं)। मैं इसे गैर-स्थिर होना पसंद करूंगा ताकि इसे उस वर्ग में इंजेक्ट किया जा सके जो इसका उपयोग कर रहा है।
फैबियो मार्रेको

जवाबों:


137

असल में, हालाँकि मैंने ब्लॉग किया ( ल्यूक का जवाब देखें ), मुझे लगता है कि यह मेरे आईडीसोपॉली रैपर से बेहतर है। विशिष्ट कोड:

Service<IOrderService>.Use(orderService=>
{
  orderService.PlaceOrder(request);
}); 

(प्रति टिप्पणी संपादित करें)

चूंकि Useरिटर्न शून्य है, रिटर्न मानों को संभालने का सबसे आसान तरीका एक कैप्चर किए गए चर के माध्यम से है:

int newOrderId = 0; // need a value for definite assignment
Service<IOrderService>.Use(orderService=>
  {
    newOrderId = orderService.PlaceOrder(request);
  });
Console.WriteLine(newOrderId); // should be updated

2
@MarcGravell मैं उस ग्राहक को कहां इंजेक्ट कर सकता हूं? मेरा मानना ​​है कि ChannelFactory क्लाइंट बनाता है, और फ़ैक्टरी ऑब्जेक्ट सर्विस क्लास के अंदर नया किया जाता है, जिसका अर्थ है कि कस्टम फ़ैक्टरी को अनुमति देने के लिए कोड को थोड़ा रिफैक्ट किया जाना चाहिए। क्या यह सही है, या मैं यहाँ कुछ स्पष्ट याद कर रहा हूँ?
अट्टू

16
आप आसानी से रैपर को संशोधित कर सकते हैं ताकि आपको परिणाम के लिए कैप्चर चर की आवश्यकता न हो। कुछ इस तरह से: public static TResult Use<TResult>(Func<T, TResult> codeBlock) { ... }
क्रिस

3
हो सकता है कि उपयोगी https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/ और https://devzone.channeladam.com/articles/2014/09/how-to-easily-call-wcf-service-properly/ और http://dzimchuk.net/post/wcf-error-helpers
PreguntonCojoneroCabrón

मैं इस तरह का उपयोग करके क्रेडेंशियल कैसे जोड़ सकता हूं?
हिप्पासस

2
मेरी राय में, सबसे सही समाधान होगा: 1) रेस की स्थिति के बिना क्लोज़ / एबोर्ट पैटर्न का प्रदर्शन करें 2) जब सर्विस ऑपरेशन अपवाद छोड़ता है तो स्थिति को संभालें 3) उन परिस्थितियों को हैंडल करें जब क्लोज और एबॉर्ट दोनों तरीके अपवाद 4 को फेंकते हैं। हैंडल एसिंक्रोनस अपवाद जैसे कि https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
थ्रेडबोर्टएक्ससेप्शन

88

IServiceOriented.com द्वारा वकालत और डेविड बैरेट के ब्लॉग द्वारा वकालत किए गए समाधान के बीच एक विकल्प को देखते हुए , मैं ग्राहक के निपटान () विधि को ओवरराइड करके पेश की गई सादगी पसंद करता हूं। यह मुझे उपयोग करने के लिए जारी रखने की अनुमति देता है () बयान के रूप में एक डिस्पोजेबल वस्तु के साथ की उम्मीद होगी। हालाँकि, जैसा कि @Brian ने बताया, इस समाधान में एक दौड़ की स्थिति है जिसमें राज्य की जाँच की जा सकती है, लेकिन जब उसे बंद किया जाता है, तब तक उसे बंद नहीं किया जा सकता है।

इसलिए, इसके चारों ओर जाने के लिए, मैंने एक ऐसा समाधान नियोजित किया है जो दोनों दुनिया के सर्वश्रेष्ठ को मिलाता है।

void IDisposable.Dispose()
{
    bool success = false;
    try 
    {
        if (State != CommunicationState.Faulted) 
        {
            Close();
            success = true;
        }
    } 
    finally 
    {
        if (!success) 
            Abort();
    }
}

2
अनवांटेड संसाधनों के साथ 'ट्राय-एक्टीव' (या सिन्थेटिक शुगर - "उपयोग करना () {}") स्टेटमेंट का उपयोग करना जोखिम भरा नहीं है? बिंदु में मामला, यदि "क्लोज" विकल्प विफल हो जाता है, तो अपवाद पकड़ा नहीं जाता है, और अंत में नहीं चल सकता है। इसके अलावा, अगर अंत में बयान में कोई अपवाद है तो यह अन्य अपवादों को खत्म कर सकता है। मुझे लगता है कि यही कारण है कि ट्राई-कैच को प्राथमिकता दी जाती है।
ज़ैक जैन्सेन

ज़ैक, अपनी वस्तु पर स्पष्ट नहीं; मैं क्या खो रहा हूँ? यदि क्लोज़ विधि एक अपवाद फेंकता है, तो अंत में अपवाद को फेंकने से पहले निष्पादित होगा। सही?
पैट्रिक स्ज़लाप्स्की

1
@jmoreno, मैं आपके संपादन को पूर्ववत् करता हूं। यदि आप देखेंगे, तो विधि में कोई भी कैच ब्लॉक नहीं है। विचार यह है कि जो भी अपवाद होता है (अंत में भी) फेंक दिया जाना चाहिए, चुपचाप पकड़ा नहीं जाना चाहिए।
मैट डेविस

5
@MattDavis आपको successध्वज की आवश्यकता क्यों है ? क्यों नहीं try { Close(); } catch { Abort(); throw; }?
कॉन्स्टेंटिन स्पिरिन 1

के बारे में एक कोशिश डाल / पकड़ने के बारे में क्या Close(); success = true;? मैं एक अपवाद नहीं फेंकना चाहता अगर मैं इसे अंतत: ब्लॉक में सफलतापूर्वक समाप्त कर सकता। मैं केवल एक अपवाद चाहता हूँ अगर एबॉर्ट () उस मामले में विफल रहा। इस तरह, कोशिश / कैच संभावित दौड़ की स्थिति के अपवाद को छिपाएगा और फिर भी आपको अंत में ब्लॉक में गर्भपात () करने की अनुमति देगा।
गोकू_डा_मास्टर

32

मैंने इसे सही काम करने के लिए एक उच्च आदेश फ़ंक्शन लिखा । हमने कई परियोजनाओं में इसका उपयोग किया है और यह बहुत अच्छा काम कर रहा है। यह है कि चीजों को शुरू से ही किया जाना चाहिए, "प्रतिमान" का उपयोग किए बिना या इतने पर।

TReturn UseService<TChannel, TReturn>(Func<TChannel, TReturn> code)
{
    var chanFactory = GetCachedFactory<TChannel>();
    TChannel channel = chanFactory.CreateChannel();
    bool error = true;
    try {
        TReturn result = code(channel);
        ((IClientChannel)channel).Close();
        error = false;
        return result;
    }
    finally {
        if (error) {
            ((IClientChannel)channel).Abort();
        }
    }
}

आप इस तरह से कॉल कर सकते हैं:

int a = 1;
int b = 2;
int sum = UseService((ICalculator calc) => calc.Add(a, b));
Console.WriteLine(sum);

यह बहुत ज्यादा है जैसे आप अपने उदाहरण में हैं। कुछ परियोजनाओं में, हम दृढ़ता से टाइप किए गए सहायक तरीके लिखते हैं, इसलिए हम "Wcf.UseFooService (f => f ...)" जैसी चीजें लिखते हैं।

मुझे यह बहुत सुंदर लगता है, सभी चीजों पर विचार किया जाता है। क्या आपके सामने कोई विशेष समस्या है?

यह अन्य निफ्टी सुविधाओं को प्लग इन करने की अनुमति देता है। उदाहरण के लिए, एक साइट पर, साइट उपयोगकर्ता की ओर से लॉग इन की सेवा को प्रमाणित करती है। (साइट की खुद से कोई प्रमाणिकता नहीं है।) अपनी खुद की "UseService" विधि सहायक लिखकर, हम चैनल फैक्ट्री को उस तरह से कॉन्फ़िगर कर सकते हैं, जिस तरह से हम चाहते हैं, आदि। हम उत्पन्न प्रॉक्सी का उपयोग करने के लिए भी बाध्य नहीं हैं - कोई भी इंटरफ़ेस करेगा ।


मुझे अपवाद मिल रहा है: ChannelFactory.Endpoint पर पता गुण शून्य था। ChannelFactory के समापन बिंदु में एक वैध पता निर्दिष्ट होना चाहिएGetCachedFactoryविधि क्या है ?
मार्शल

28

WCF क्लाइंट कॉल को संभालने के लिए यह Microsoft का अनुशंसित तरीका है:

अधिक विवरण के लिए देखें: अपेक्षित अपवाद

try
{
    ...
    double result = client.Add(value1, value2);
    ...
    client.Close();
}
catch (TimeoutException exception)
{
    Console.WriteLine("Got {0}", exception.GetType());
    client.Abort();
}
catch (CommunicationException exception)
{
    Console.WriteLine("Got {0}", exception.GetType());
    client.Abort();
}

अतिरिक्त जानकारी बहुत से लोग WCF पर यह सवाल पूछते हुए प्रतीत होते हैं कि Microsoft ने अपवादों को संभालने के तरीके को प्रदर्शित करने के लिए एक समर्पित नमूना भी बनाया था:

c: \ WF_WCF_Samples \ WCF \ बेसिक \ क्लाइंट \ ExpectedExceptions \ सीएस \ ग्राहक

नमूना डाउनलोड करें: C # या VB

इस बात पर विचार करते हुए कि इस कथन पर बहुत सारे मुद्दों का उपयोग किया जा रहा है , (गर्म?) इस मुद्दे पर आंतरिक चर्चा और सूत्र हैं , मैं अपना समय बर्बाद नहीं करने जा रहा हूं, कोड काउबॉय बनने और एक क्लीनर तरीका खोजने की कोशिश कर रहा हूं। मैं बस इसे चूसूंगा, और अपने सर्वर एप्लिकेशन के लिए इस क्रिया (अभी तक विश्वसनीय) तरीके से WCF क्लाइंट को लागू करूंगा।

वैकल्पिक अतिरिक्त विफलता को पकड़ने के लिए

कई अपवाद प्राप्त होते हैं CommunicationExceptionऔर मुझे नहीं लगता कि उन अपवादों में से अधिकांश को वापस लिया जाना चाहिए। मैंने MSDN पर प्रत्येक अपवाद के माध्यम से ड्रॉडाउन किया और फिर से प्राप्त होने योग्य अपवादों ( TimeOutExceptionऊपर के अलावा ) की एक छोटी सूची मिली । क्या मुझे पता है कि क्या मैं एक अपवाद को याद करता हूं जिसे वापस लिया जाना चाहिए।

  // The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
catch (ChannelTerminatedException cte)
{
secureSecretService.Abort();
// todo: Implement delay (backoff) and retry
}

// The following is thrown when a remote endpoint could not be found or reached.  The endpoint may not be found or 
// reachable because the remote endpoint is down, the remote endpoint is unreachable, or because the remote network is unreachable.
catch (EndpointNotFoundException enfe)
{
secureSecretService.Abort();
// todo: Implement delay (backoff) and retry
}

// The following exception that is thrown when a server is too busy to accept a message.
catch (ServerTooBusyException stbe)
{
secureSecretService.Abort();
// todo: Implement delay (backoff) and retry
}

बेशक, यह लिखने के लिए सांसारिक कोड का एक सा है। मैं वर्तमान में इस उत्तर को पसंद करता हूं , और उस कोड में कोई "हैक" नहीं देखता जो सड़क के नीचे के मुद्दों का कारण हो सकता है।


1
क्या नमूना से कोड अभी भी समस्या पैदा कर रहा है? मैंने प्रयोग परियोजना (VS2013) को चलाने की कोशिश की, लेकिन "Hope this code wasn't important, because it might not happen."अभी भी इस लाइन को अंजाम दिया जा रहा है ...
janv8000

14

मैंने आखिरकार इस समस्या के स्वच्छ समाधान की दिशा में कुछ ठोस कदम उठाए हैं।

यह कस्टम टूल एक अपवाद हैंडलिंग प्रॉक्सी प्रदान करने के लिए WCFProxyGenerator को विस्तारित करता है। यह एक अतिरिक्त प्रॉक्सी बनाता है ExceptionHandlingProxy<T>जिसे विरासत में मिला है ExceptionHandlingProxyBase<T>- जो बाद वाला प्रॉक्सी की कार्यक्षमता का मांस लागू करता है। इसका परिणाम यह है कि आप उस डिफ़ॉल्ट प्रॉक्सी का उपयोग करना चुन सकते हैं जो विरासत में मिलता है ClientBase<T>या ExceptionHandlingProxy<T>जो चैनल फैक्ट्री और चैनल के जीवनकाल का प्रबंधन करता है। ExceptionHandlingProxy अतुल्यकालिक विधियों और संग्रह प्रकारों के संबंध में सेवा चयन संवाद जोड़ें में अपने चयन का सम्मान करता है।

कोडप्लेक्स में एक्सेप्शन हैंडलिंग डब्ल्यूसीएफ प्रॉक्सी जनरेटर नामक एक परियोजना है । यह मूल रूप से विजुअल स्टूडियो 2008 में एक नया कस्टम टूल स्थापित करता है, फिर इस टूल का उपयोग नई सेवा प्रॉक्सी (सेवा संदर्भ जोड़ें) उत्पन्न करने के लिए करता है । इसमें दोषपूर्ण चैनल, टाइमआउट और सुरक्षित निपटान से निपटने के लिए कुछ अच्छी कार्यक्षमता है। यहाँ एक उत्कृष्ट वीडियो है जिसे एक्ससेप्शनहैंडलिंगप्रॉक्सीवॉपर कहा जाता है जो यह बताता है कि यह कैसे काम करता है।

आप सुरक्षित रूप Usingसे फिर से स्टेटमेंट का उपयोग कर सकते हैं , और यदि चैनल किसी भी अनुरोध (टाइमआउट एक्सेप्शन या कम्यूनिकेशन अपवाद) पर गलत है, तो रैपर गलत चैनल को फिर से इनिशियलाइज़ करेगा और क्वेरी को पुन: प्रयास करेगा। यदि वह विफल हो जाता है तो यह Abort()कमांड को कॉल करेगा और प्रॉक्सी को डिस्पोज़ करेगा और एक्सेप्शन को रीथ्रो करेगा। यदि सेवा एक FaultExceptionकोड को फेंकती है तो यह निष्पादित करना बंद कर देगी, और प्रॉक्सी को सही अपवाद को सुरक्षित रूप से फेंक दिया जाएगा जैसा कि अपेक्षित था।


@ शमी स्टेटस बीटा दिनांक: शनि जुलाई 11, 2009 मिशेल Bustamante द्वारा । डेड प्रोजेक्ट?
किकेनेट

11

मार्क ग्रेवेल, माइकलजीजी और मैट डेविस के जवाबों के आधार पर, हमारे डेवलपर्स निम्नलिखित के साथ आए:

public static class UsingServiceClient
{
    public static void Do<TClient>(TClient client, Action<TClient> execute)
        where TClient : class, ICommunicationObject
    {
        try
        {
            execute(client);
        }
        finally
        {
            client.DisposeSafely();
        }
    }

    public static void DisposeSafely(this ICommunicationObject client)
    {
        if (client == null)
        {
            return;
        }

        bool success = false;

        try
        {
            if (client.State != CommunicationState.Faulted)
            {
                client.Close();
                success = true;
            }
        }
        finally
        {
            if (!success)
            {
                client.Abort();
            }
        }
    }
}

उपयोग का उदाहरण:

string result = string.Empty;

UsingServiceClient.Do(
    new MyServiceClient(),
    client =>
    result = client.GetServiceResult(parameters));

यह संभव के रूप में "सिंटैक्स" का उपयोग करने के करीब है, आपको शून्य विधि को कॉल करते समय एक डमी मूल्य वापस नहीं करना पड़ता है, और आप ट्यूपल्स का उपयोग किए बिना सेवा को कई कॉल कर सकते हैं (और कई मान लौटा सकते हैं)।

इसके अलावा, ClientBase<T>यदि आप चाहें तो ChannelFactory के बजाय वंश के साथ इसका उपयोग कर सकते हैं ।

यदि कोई डेवलपर मैन्युअल रूप से किसी प्रॉक्सी / चैनल का निपटान करना चाहता है, तो एक्सटेंशन विधि उजागर हो जाती है।


अगर मैं पूलिंगडुप्लेक्स का उपयोग कर रहा हूं और कॉल के बाद कनेक्शन बंद नहीं करता हूं, तो इसका उपयोग करना समझ में आता है, इसलिए मेरी क्लाइंट सेवा कुछ दिन भी रह सकती है और सर्वर कॉलबैक को संभाल सकती है। जहां तक ​​मैं समझता हूं कि समाधान जो यहां चर्चा की गई है, प्रति सत्र एक कॉल के लिए समझ में आता है?
sll

@sll - यह कॉल रिटर्न (प्रति सत्र एक कॉल) के तुरंत बाद कनेक्शन बंद करने के लिए है।
ट्रूविल

@ कछू DisposeSafelyनिजी बनाना निश्चित रूप से एक विकल्प है, और भ्रम से बचना होगा। ऐसे मामलों का उपयोग हो सकता है जहां कोई इसे सीधे कॉल करना चाहेगा, लेकिन मैं एक बंद नहीं कर सकता।
ट्रू वील

@truewill सिर्फ दस्तावेज़ीकरण के लिए, यह उल्लेख करना भी महत्वपूर्ण है कि यह विधि थ्रेड-सुरक्षित है?
काछो सांता

1
मेरी राय में, सबसे सही समाधान होगा: 1) रेस की स्थिति के बिना क्लोज़ / एबोर्ट पैटर्न का प्रदर्शन करें 2) जब सर्विस ऑपरेशन अपवाद छोड़ता है तो स्थिति को संभालें 3) उन परिस्थितियों को हैंडल करें जब क्लोज और एबॉर्ट दोनों तरीके अपवाद 4 को फेंकते हैं। हैंडल एसिंक्रोनस अपवाद जैसे कि https://devzone.channeladam.com/articles/2014/07/how-to-call-wcf-service-properly/
थ्रेडएबोर्टएक्ससेप्शन

8

@ मारक ग्रेवेल

क्या इसका उपयोग करना ठीक नहीं होगा:

public static TResult Using<T, TResult>(this T client, Func<T, TResult> work)
        where T : ICommunicationObject
{
    try
    {
        var result = work(client);

        client.Close();

        return result;
    }
    catch (Exception e)
    {
        client.Abort();

        throw;
    }
}

या, के (Func<T, TResult>)मामले में एक ही बात हैService<IOrderService>.Use

ये रिटर्निंग वैरिएबल को आसान बनाते हैं।


2
+1 @MarcGravell मुझे लगता है कि आपका उत्तर 'और भी बेहतर कर सकता है': P (और एक्शन को फन के संदर्भ में शून्य रिटर्न के साथ लागू किया जा सकता है)। यह पूरा पृष्ठ एक गड़बड़ है - अगर मैं इस दशक में किसी भी समय WCF का उपयोग करने की परिकल्पना करता हूं, तो मैं एक एकीकृत रूप दूंगा और टिप्पणी
करूंगा

7

यह क्या है?

यह स्वीकृत उत्तर का सीडब्ल्यू संस्करण है लेकिन (जिसे मैं पूर्ण मानता हूं) अपवाद हैंडलिंग शामिल है।

स्वीकृत उत्तर इस वेबसाइट का संदर्भ देता है जो अब आसपास नहीं है । आपको परेशानी से बचाने के लिए, मैं यहां सबसे अधिक प्रासंगिक भागों को शामिल कर रहा हूं। इसके अलावा, मैंने उन pesky नेटवर्क टाइमआउट को संभालने के लिए अपवाद पुनर्प्रयास हैंडलिंग को शामिल करने के लिए इसे थोड़ा संशोधित किया ।

सरल WCF क्लाइंट उपयोग

एक बार जब आप अपना क्लाइंट साइड प्रॉक्सी जनरेट करते हैं, तो आपको इसे लागू करने की आवश्यकता है।

Service<IOrderService>.Use(orderService=>
{
  orderService.PlaceOrder(request);
});

ServiceDelegate.cs

इस फ़ाइल को अपने समाधान में जोड़ें। इस फ़ाइल में किसी भी तरह के बदलाव की आवश्यकता नहीं है, जब तक कि आप रिट्रीट की संख्या को बदलना नहीं चाहते हैं या आप क्या अपवादों को संभालना चाहते हैं।

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;


       Exception mostRecentEx = null;
       int millsecondsToSleep = 1000;

       for(int i=0; i<5; i++)  // Attempt a maximum of 5 times 
       {
           try
           {
               codeBlock((T)proxy);
               proxy.Close();
               success = true; 
               break;
           }

           // The following is typically thrown on the client when a channel is terminated due to the server closing the connection.
           catch (ChannelTerminatedException cte)
           {
              mostRecentEx = cte;
               proxy.Abort();
               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep  * (i + 1)); 
           }

           // The following is thrown when a remote endpoint could not be found or reached.  The endpoint may not be found or 
           // reachable because the remote endpoint is down, the remote endpoint is unreachable, or because the remote network is unreachable.
           catch (EndpointNotFoundException enfe)
           {
              mostRecentEx = enfe;
               proxy.Abort();
               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }

           // The following exception that is thrown when a server is too busy to accept a message.
           catch (ServerTooBusyException stbe)
           {
              mostRecentEx = stbe;
               proxy.Abort();

               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }
           catch (TimeoutException timeoutEx)
           {
               mostRecentEx = timeoutEx;
               proxy.Abort();

               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           } 
           catch (CommunicationException comException)
           {
               mostRecentEx = comException;
               proxy.Abort();

               //  delay (backoff) and retry 
               Thread.Sleep(millsecondsToSleep * (i + 1)); 
           }
           catch(Exception )
           {
                // rethrow any other exception not defined here
                // You may want to define a custom Exception class to pass information such as failure count, and failure type
                proxy.Abort();
                throw ;  
           }
       }
       if (success == false && mostRecentEx != null) 
       { 
           proxy.Abort();
           throw new Exception("WCF call failed after 5 retries.", mostRecentEx );
       }

    }
}

पुनश्च: मैंने इस पोस्ट को एक सामुदायिक विकि बना दिया है। मैं इस उत्तर से "अंक" एकत्र नहीं करूंगा, लेकिन यदि आप कार्यान्वयन से सहमत हैं, या इसे बेहतर बनाने के लिए इसे संपादित करते हैं, तो इसे प्राथमिकता दें।


मुझे यकीन नहीं है कि मैं इस जवाब के आपके लक्षण वर्णन से सहमत हूं। यह अपवाद को संभालने के आपके विचार के साथ CW संस्करण है।
जॉन सॉन्डर्स

@ जॉनसंडर्स - सच (अपवाद को संभालने की मेरी अवधारणा)। मुझे किसी भी अपवाद के बारे में बताएं जो मैं याद कर रहा हूं या गलत तरीके से संभाल रहा हूं।
goodguys_activate

सफलता चर के बारे में क्या है? इसे स्रोत कोड में जोड़ना होगा: यदि (सफलता) वापसी; ??
किकेनेट

यदि पहला कॉल फेंकता है और दूसरा सफल हो जाता है तो ज्यादातरRecentEx शून्य नहीं होगा, इसलिए आप एक अपवाद को फेंक रहे हैं जो कि 5 रिट्रीव्स को विफल कर देता है। या क्या मैं कुछ न कुछ भूल रहा हूं? मैं यह नहीं देखता कि आप 2, 3, 4 वें या 5 वें प्रयास में सफल होने पर सबसे अधिक स्पष्ट कैसे करें। इसके अलावा एक वापसी ओ सफल नहीं देखते हैं। मुझे यहां कुछ याद आ रहा है, लेकिन कोई अपवाद नहीं फेंके जाने पर यह कोड हमेशा 5 बार चलेगा?
बार्ट कैलिक्सो

@ बर्ट - मैंने success == falseबयान में कहा कि अगर फाइनल हुआ
goodguys_activate

7

नीचे सवाल से स्रोत का एक बढ़ाया संस्करण है और कई चैनल कारखानों को कैश करने और अनुबंध नाम से कॉन्फ़िगरेशन फ़ाइल में एंडपॉइंट को देखने का प्रयास करने के लिए विस्तारित किया गया है।

यह .NET 4 का उपयोग करता है (विशेष रूप से: विपरीत, LINQ, var):

/// <summary>
/// Delegate type of the service method to perform.
/// </summary>
/// <param name="proxy">The service proxy.</param>
/// <typeparam name="T">The type of service to use.</typeparam>
internal delegate void UseServiceDelegate<in T>(T proxy);

/// <summary>
/// Wraps using a WCF service.
/// </summary>
/// <typeparam name="T">The type of service to use.</typeparam>
internal static class Service<T>
{
    /// <summary>
    /// A dictionary to hold looked-up endpoint names.
    /// </summary>
    private static readonly IDictionary<Type, string> cachedEndpointNames = new Dictionary<Type, string>();

    /// <summary>
    /// A dictionary to hold created channel factories.
    /// </summary>
    private static readonly IDictionary<string, ChannelFactory<T>> cachedFactories =
        new Dictionary<string, ChannelFactory<T>>();

    /// <summary>
    /// Uses the specified code block.
    /// </summary>
    /// <param name="codeBlock">The code block.</param>
    internal static void Use(UseServiceDelegate<T> codeBlock)
    {
        var factory = GetChannelFactory();
        var proxy = (IClientChannel)factory.CreateChannel();
        var success = false;

        try
        {
            using (proxy)
            {
                codeBlock((T)proxy);
            }

            success = true;
        }
        finally
        {
            if (!success)
            {
                proxy.Abort();
            }
        }
    }

    /// <summary>
    /// Gets the channel factory.
    /// </summary>
    /// <returns>The channel factory.</returns>
    private static ChannelFactory<T> GetChannelFactory()
    {
        lock (cachedFactories)
        {
            var endpointName = GetEndpointName();

            if (cachedFactories.ContainsKey(endpointName))
            {
                return cachedFactories[endpointName];
            }

            var factory = new ChannelFactory<T>(endpointName);

            cachedFactories.Add(endpointName, factory);
            return factory;
        }
    }

    /// <summary>
    /// Gets the name of the endpoint.
    /// </summary>
    /// <returns>The name of the endpoint.</returns>
    private static string GetEndpointName()
    {
        var type = typeof(T);
        var fullName = type.FullName;

        lock (cachedFactories)
        {
            if (cachedEndpointNames.ContainsKey(type))
            {
                return cachedEndpointNames[type];
            }

            var serviceModel = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None).SectionGroups["system.serviceModel"] as ServiceModelSectionGroup;

            if ((serviceModel != null) && !string.IsNullOrEmpty(fullName))
            {
                foreach (var endpointName in serviceModel.Client.Endpoints.Cast<ChannelEndpointElement>().Where(endpoint => fullName.EndsWith(endpoint.Contract)).Select(endpoint => endpoint.Name))
                {
                    cachedEndpointNames.Add(type, endpointName);
                    return endpointName;
                }
            }
        }

        throw new InvalidOperationException("Could not find endpoint element for type '" + fullName + "' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this name could be found in the client element.");
    }
}

1
के UseServiceDelegate<T>बजाय का उपयोग क्यों करें Action<T>?
माइक मेयर

1
एकमात्र कारण मैं सोच सकता हूं कि मूल लेखक ने ऐसा किया था कि एक जोरदार-टाइप प्रतिनिधि को डेवलपर को पता होगा कि वह एक सेवा को कॉल करने से संबंधित है। लेकिन, जहां तक ​​मैं देख सकता हूं, Action<T>काम करता है।
जेसी सी। स्लाइसर

5

इस तरह एक आवरण काम करेगा:

public class ServiceClientWrapper<ServiceType> : IDisposable
{
    private ServiceType _channel;
    public ServiceType Channel
    {
        get { return _channel; }
    }

    private static ChannelFactory<ServiceType> _channelFactory;

    public ServiceClientWrapper()
    {
        if(_channelFactory == null)
             // Given that the endpoint name is the same as FullName of contract.
            _channelFactory = new ChannelFactory<ServiceType>(typeof(T).FullName);
        _channel = _channelFactory.CreateChannel();
        ((IChannel)_channel).Open();
    }

    public void Dispose()
    {
        try
        {
            ((IChannel)_channel).Close();
        }
        catch (Exception e)
        {
            ((IChannel)_channel).Abort();
            // TODO: Insert logging
        }
    }
}

आपको कोड लिखने में सक्षम होना चाहिए जैसे:

ResponseType response = null;
using(var clientWrapper = new ServiceClientWrapper<IService>())
{
    var request = ...
    response = clientWrapper.Channel.MyServiceCall(request);
}
// Use your response object.

यदि आवश्यक हो तो रैपर अधिक अपवादों को पकड़ सकता है, लेकिन सिद्धांत समान रहता है।


मुझे याद है कि डिस्कशन के संबंध में चर्चा कुछ शर्तों के तहत नहीं की जा रही है ... जिसके परिणामस्वरूप मेमोरी लीक w / WCF है।
goodguys_activate

मुझे यकीन नहीं है कि यह स्मृति लीक में परिणामी था, लेकिन समस्या यह है। जब आप DisposeIChannel पर कॉल करते हैं तो यह एक अपवाद फेंक सकता है यदि चैनल एक दोषपूर्ण स्थिति में है, यह एक समस्या है क्योंकि Microsoft निर्दिष्ट करता है कि Disposeकभी भी फेंकना नहीं चाहिए। तो क्या ऊपर दिया गया कोड Closeअपवाद को फेंकने पर मामले को संभाल रहा है। यदि Abortफेंकता है तो यह कुछ गंभीर रूप से गलत हो सकता है। मैंने इसके बारे में पिछले दिसंबर में एक ब्लॉग पोस्ट लिखा था: blog.tomasjansson.com/2010/12/disposible-wcf-client-wrapper
टॉमस जैनसन

4

मैंने डिस्पोज़ () समस्या को हल करने के लिए कैसल डायनेमिक प्रॉक्सी का उपयोग किया, और चैनल को ऑटो-रिफ्रेश करने के लिए भी लागू किया जब वह अनुपयोगी अवस्था में हो। इसका उपयोग करने के लिए आपको एक नया इंटरफ़ेस बनाना होगा जो आपके सर्विस कॉन्ट्रैक्ट और आईडीसोपायरी को विरासत में मिले। डायनेमिक प्रॉक्सी इस इंटरफ़ेस को लागू करता है और WCF चैनल को लपेटता है:

Func<object> createChannel = () =>
    ChannelFactory<IHelloWorldService>
        .CreateChannel(new NetTcpBinding(), new EndpointAddress(uri));
var factory = new WcfProxyFactory();
var proxy = factory.Create<IDisposableHelloWorldService>(createChannel);
proxy.HelloWorld();

मुझे यह पसंद है क्योंकि आप WCF सेवाओं को उपभोक्ताओं के बिना WCF के किसी भी विवरण के बारे में चिंता करने की आवश्यकता के बिना इंजेक्ट कर सकते हैं। और अन्य समाधानों की तरह कोई जोड़ नहीं है।

कोड पर एक नज़र है, यह वास्तव में बहुत आसान है: WCF डायनेमिक प्रॉक्सी


4

एक्सटेंशन विधि का उपयोग करें:

public static class CommunicationObjectExtensions
{
    public static TResult MakeSafeServiceCall<TResult, TService>(this TService client, Func<TService, TResult> method) where TService : ICommunicationObject
    {
        TResult result;

        try
        {
            result = method(client);
        }
        finally
        {
            try
            {
                client.Close();
            }
            catch (CommunicationException)
            {
                client.Abort(); // Don't care about these exceptions. The call has completed anyway.
            }
            catch (TimeoutException)
            {
                client.Abort(); // Don't care about these exceptions. The call has completed anyway.
            }
            catch (Exception)
            {
                client.Abort();
                throw;
            }
        }

        return result;
    }
}

4

यदि आपको IoC की आवश्यकता नहीं है या आप ऑटोजेनरेटेड क्लाइंट (सर्विस रेफरेंस) का उपयोग कर रहे हैं, तो आप क्लोजर को प्रबंधित करने के लिए एक साधारण आवरण का उपयोग कर सकते हैं और GC को क्लाइंटबेस को ले जाने दे सकते हैं जब यह एक सुरक्षित स्थिति में होता है जो किसी अपवाद को नहीं फेंकेगा। जीसी सेवा में डिस्क्लेमर को कॉल करेगा, और यह कॉल करेगा Close। चूंकि यह बंद है, इसलिए इससे कोई नुकसान नहीं हो सकता। मैं उत्पादन कोड में समस्याओं के बिना इसका उपयोग कर रहा हूं।

public class AutoCloseWcf : IDisposable
{

    private ICommunicationObject CommunicationObject;

    public AutoDisconnect(ICommunicationObject CommunicationObject)
    {
        this.CommunicationObject = CommunicationObject;
    }

    public void Dispose()
    {
        if (CommunicationObject == null)
            return;
        try {
            if (CommunicationObject.State != CommunicationState.Faulted) {
                CommunicationObject.Close();
            } else {
                CommunicationObject.Abort();
            }
        } catch (CommunicationException ce) {
            CommunicationObject.Abort();
        } catch (TimeoutException toe) {
            CommunicationObject.Abort();
        } catch (Exception e) {
            CommunicationObject.Abort();
            //Perhaps log this

        } finally {
            CommunicationObject = null;
        }
    }
}

फिर जब आप सर्वर तक पहुंच रहे हैं, तो आप क्लाइंट बनाते हैं और usingऑटोडिसकोनेक्ट में उपयोग करते हैं:

var Ws = new ServiceClient("netTcpEndPointName");
using (new AutoCloseWcf(Ws)) {

    Ws.Open();

    Ws.Test();
}

3

सारांश

इस उत्तर में वर्णित तकनीकों का उपयोग करके निम्नलिखित वाक्य रचना के साथ एक ब्लॉक में एक WCF सेवा का उपभोग कर सकते हैं:

var channelFactory = new ChannelFactory<IMyService>("");

var serviceHelper = new ServiceHelper<IMyService>(channelFactory);
var proxy = serviceHelper.CreateChannel();
using (proxy as IDisposable)
{
    proxy.DoWork();
}

आप निश्चित रूप से अपनी स्थिति के लिए और अधिक संक्षिप्त प्रोग्रामिंग मॉडल प्राप्त करने के लिए इसे और भी अनुकूल कर सकते हैं - लेकिन मुद्दा यह है कि हम IMyServiceचैनल को फिर से लागू करने का एक कार्यान्वयन बना सकते हैं जो सही ढंग से डिस्पोजेबल पैटर्न को लागू करता है।


विवरण

इस प्रकार दिए गए सभी जवाब डब्ल्यूसीएफ चैनल के कार्यान्वयन में "बग" के आसपास होने की समस्या को दूर करते हैं IDisposable। इसका उत्तर सबसे संक्षिप्त प्रोग्रामिंग मॉडल (आपको usingअप्रबंधित संसाधनों पर निपटान के लिए ब्लॉक का उपयोग करने की अनुमति देता है) प्रदान करने के लिए लगता है, यह एक है - जहां प्रॉक्सी को IDisposableबग-मुक्त कार्यान्वयन के साथ संशोधित किया गया है। इस दृष्टिकोण के साथ समस्या स्थिरता है - हमें इस कार्यक्षमता को कभी भी हमारे द्वारा उपयोग किए जाने वाले प्रॉक्सी के लिए फिर से लागू करना होगा। इस उत्तर की भिन्नता पर हम देखेंगे कि हम इस तकनीक को सामान्य बनाने के लिए विरासत के बजाय रचना का उपयोग कैसे कर सकते हैं ।

पहला प्रयास

कार्यान्वयन के लिए विभिन्न कार्यान्वयन प्रतीत होते हैं IDisposable, लेकिन तर्क के लिए हम वर्तमान में स्वीकृत उत्तर द्वारा उपयोग किए जाने वाले अनुकूलन का उपयोग करेंगे ।

[ServiceContract]
public interface IMyService
{
    [OperationContract]
    void DoWork();
}

public class ProxyDisposer : IDisposable
{
    private IClientChannel _clientChannel;


    public ProxyDisposer(IClientChannel clientChannel)
    {
        _clientChannel = clientChannel;
    }

    public void Dispose()
    {
        var success = false;
        try
        {
            _clientChannel.Close();
            success = true;
        }
        finally
        {
            if (!success)
                _clientChannel.Abort();
            _clientChannel = null;
        }
    }
}

public class ProxyWrapper : IMyService, IDisposable
{
    private IMyService _proxy;
    private IDisposable _proxyDisposer;

    public ProxyWrapper(IMyService proxy, IDisposable disposable)
    {
        _proxy = proxy;
        _proxyDisposer = disposable;
    }

    public void DoWork()
    {
        _proxy.DoWork();
    }

    public void Dispose()
    {
        _proxyDisposer.Dispose();
    }
}

उपरोक्त वर्गों के साथ सशस्त्र हम अब लिख सकते हैं

public class ServiceHelper
{
    private readonly ChannelFactory<IMyService> _channelFactory;

    public ServiceHelper(ChannelFactory<IMyService> channelFactory )
    {
        _channelFactory = channelFactory;
    }

    public IMyService CreateChannel()
    {
        var channel = _channelFactory.CreateChannel();
        var channelDisposer = new ProxyDisposer(channel as IClientChannel);
        return new ProxyWrapper(channel, channelDisposer);
    }
}

यह हमें usingब्लॉक का उपयोग करके हमारी सेवा का उपभोग करने की अनुमति देता है :

ServiceHelper serviceHelper = ...;
var proxy = serviceHelper.CreateChannel();
using (proxy as IDisposable)
{
    proxy.DoWork();
}

यह सामान्य बना रहा है

हमने अब तक टॉमस के समाधान में सुधार किया है । इस कोड को जेनेरिक होने से रोकता है यह तथ्य यह है कि ProxyWrapperवर्ग को हर सेवा अनुबंध के लिए फिर से लागू किया जाना चाहिए जो हम चाहते हैं। अब हम एक ऐसे वर्ग को देखेंगे जो हमें IL का उपयोग करके गतिशील रूप से इस प्रकार का निर्माण करने की अनुमति देता है:

public class ServiceHelper<T>
{
    private readonly ChannelFactory<T> _channelFactory;

    private static readonly Func<T, IDisposable, T> _channelCreator;

    static ServiceHelper()
    {
        /** 
         * Create a method that can be used generate the channel. 
         * This is effectively a compiled verion of new ProxyWrappper(channel, channelDisposer) for our proxy type
         * */
        var assemblyName = Guid.NewGuid().ToString();
        var an = new AssemblyName(assemblyName);
        var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
        var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName);

        var proxyType = CreateProxyType(moduleBuilder, typeof(T), typeof(IDisposable));

        var channelCreatorMethod = new DynamicMethod("ChannelFactory", typeof(T),
            new[] { typeof(T), typeof(IDisposable) });

        var ilGen = channelCreatorMethod.GetILGenerator();
        var proxyVariable = ilGen.DeclareLocal(typeof(T));
        var disposableVariable = ilGen.DeclareLocal(typeof(IDisposable));
        ilGen.Emit(OpCodes.Ldarg, proxyVariable);
        ilGen.Emit(OpCodes.Ldarg, disposableVariable);
        ilGen.Emit(OpCodes.Newobj, proxyType.GetConstructor(new[] { typeof(T), typeof(IDisposable) }));
        ilGen.Emit(OpCodes.Ret);

        _channelCreator =
            (Func<T, IDisposable, T>)channelCreatorMethod.CreateDelegate(typeof(Func<T, IDisposable, T>));

    }

    public ServiceHelper(ChannelFactory<T> channelFactory)
    {
        _channelFactory = channelFactory;
    }

    public T CreateChannel()
    {
        var channel = _channelFactory.CreateChannel();
        var channelDisposer = new ProxyDisposer(channel as IClientChannel);
        return _channelCreator(channel, channelDisposer);
    }

   /**
    * Creates a dynamic type analogous to ProxyWrapper, implementing T and IDisposable.
    * This method is actually more generic than this exact scenario.
    * */
    private static Type CreateProxyType(ModuleBuilder moduleBuilder, params Type[] interfacesToInjectAndImplement)
    {
        TypeBuilder tb = moduleBuilder.DefineType(Guid.NewGuid().ToString(),
            TypeAttributes.Public | TypeAttributes.Class);

        var typeFields = interfacesToInjectAndImplement.ToDictionary(tf => tf,
            tf => tb.DefineField("_" + tf.Name, tf, FieldAttributes.Private));

        #region Constructor

        var constructorBuilder = tb.DefineConstructor(
            MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName |
            MethodAttributes.RTSpecialName,
            CallingConventions.Standard,
            interfacesToInjectAndImplement);

        var il = constructorBuilder.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0]));

        for (var i = 1; i <= interfacesToInjectAndImplement.Length; i++)
        {
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg, i);
            il.Emit(OpCodes.Stfld, typeFields[interfacesToInjectAndImplement[i - 1]]);
        }
        il.Emit(OpCodes.Ret);

        #endregion

        #region Add Interface Implementations

        foreach (var type in interfacesToInjectAndImplement)
        {
            tb.AddInterfaceImplementation(type);
        }

        #endregion

        #region Implement Interfaces

        foreach (var type in interfacesToInjectAndImplement)
        {
            foreach (var method in type.GetMethods())
            {
                var methodBuilder = tb.DefineMethod(method.Name,
                    MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig |
                    MethodAttributes.Final | MethodAttributes.NewSlot,
                    method.ReturnType,
                    method.GetParameters().Select(p => p.ParameterType).ToArray());
                il = methodBuilder.GetILGenerator();

                if (method.ReturnType == typeof(void))
                {
                    il.Emit(OpCodes.Nop);
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldfld, typeFields[type]);
                    il.Emit(OpCodes.Callvirt, method);
                    il.Emit(OpCodes.Ret);
                }
                else
                {
                    il.DeclareLocal(method.ReturnType);

                    il.Emit(OpCodes.Nop);
                    il.Emit(OpCodes.Ldarg_0);
                    il.Emit(OpCodes.Ldfld, typeFields[type]);

                    var methodParameterInfos = method.GetParameters();
                    for (var i = 0; i < methodParameterInfos.Length; i++)
                        il.Emit(OpCodes.Ldarg, (i + 1));
                    il.Emit(OpCodes.Callvirt, method);

                    il.Emit(OpCodes.Stloc_0);
                    var defineLabel = il.DefineLabel();
                    il.Emit(OpCodes.Br_S, defineLabel);
                    il.MarkLabel(defineLabel);
                    il.Emit(OpCodes.Ldloc_0);
                    il.Emit(OpCodes.Ret);
                }

                tb.DefineMethodOverride(methodBuilder, method);
            }
        }

        #endregion

        return tb.CreateType();
    }
}

हमारे नए सहायक वर्ग के साथ अब हम लिख सकते हैं

var channelFactory = new ChannelFactory<IMyService>("");

var serviceHelper = new ServiceHelper<IMyService>(channelFactory);
var proxy = serviceHelper.CreateChannel();
using (proxy as IDisposable)
{
    proxy.DoWork();
}

ध्यान दें कि आप ऑटो-जेनरेट किए गए क्लाइंट के लिए एक ही तकनीक (थोड़े संशोधनों के साथ) ClientBase<>का उपयोग कर सकते हैं (जो उपयोग करने के बजाय ) के लिए विरासत में मिला है ChannelFactory<>, या यदि आप IDisposableअपने चैनल को बंद करने के लिए एक अलग कार्यान्वयन का उपयोग करना चाहते हैं ।


2

मुझे कनेक्शन बंद करने का यह तरीका पसंद है:

var client = new ProxyClient();
try
{
    ...
    client.Close();
}
finally
{
    if(client.State != CommunicationState.Closed)
        client.Abort();
}

1

मैंने एक सरल आधार वर्ग लिखा है जो इसे संभालता है। यह NuGet पैकेज के रूप में उपलब्ध है और इसका उपयोग करना काफी आसान है।

//MemberServiceClient is the class generated by SvcUtil
public class MemberServiceManager : ServiceClientBase<MemberServiceClient>
{
    public User GetUser(int userId)
    {
        return PerformServiceOperation(client => client.GetUser(userId));
    }

    //you can also check if any error occured if you can't throw exceptions       
    public bool TryGetUser(int userId, out User user)
    {
        return TryPerformServiceOperation(c => c.GetUser(userId), out user);
    }
}

VS2013-.net 4.5.1 के लिए कोई अपडेट? stackoverflow.com/a/9370880/206730 जैसे रिट्री के लिए कोई विकल्प ? -
केकेनेट

@Kiquenet मैं अब WCF पर काम नहीं कर रहा हूँ। यदि आप मुझे एक पुल अनुरोध भेजते हैं, तो मैं इसे मर्ज कर सकता हूं और पैकेज को अपडेट कर सकता हूं।
उफुक हकीओउलारिक

1
public static class Service<TChannel>
{
    public static ChannelFactory<TChannel> ChannelFactory = new ChannelFactory<TChannel>("*");

    public static TReturn Use<TReturn>(Func<TChannel,TReturn> codeBlock)
    {
        var proxy = (IClientChannel)ChannelFactory.CreateChannel();
        var success = false;
        try
        {
            var result = codeBlock((TChannel)proxy);
            proxy.Close();
            success = true;
            return result;
        }
        finally
        {
            if (!success)
            {
                proxy.Abort();
            }
        }
    }
}

तो यह अच्छी तरह से रिटर्न स्टेटमेंट लिखने की अनुमति देता है:

return Service<IOrderService>.Use(orderService => 
{ 
    return orderService.PlaceOrder(request); 
}); 

1

मैं ChannelFactory के बजाय ServiceClient का उपयोग करने के मामले में Marc Gravell के उत्तर से सेवा का कार्यान्वयन जोड़ना चाहूंगा ।

public interface IServiceConnector<out TServiceInterface>
{
    void Connect(Action<TServiceInterface> clientUsage);
    TResult Connect<TResult>(Func<TServiceInterface, TResult> channelUsage);
}

internal class ServiceConnector<TService, TServiceInterface> : IServiceConnector<TServiceInterface>
    where TServiceInterface : class where TService : ClientBase<TServiceInterface>, TServiceInterface, new()
{
    public TResult Connect<TResult>(Func<TServiceInterface, TResult> channelUsage)
    {
        var result = default(TResult);
        Connect(channel =>
        {
            result = channelUsage(channel);
        });
        return result;
    }

    public void Connect(Action<TServiceInterface> clientUsage)
    {
        if (clientUsage == null)
        {
            throw new ArgumentNullException("clientUsage");
        }
        var isChanneldClosed = false;
        var client = new TService();
        try
        {
            clientUsage(client);
            client.Close();
            isChanneldClosed = true;
        }
        finally
        {
            if (!isChanneldClosed)
            {
                client.Abort();
            }
        }
    }
}

1

उन लोगों के लिए, यहां स्वीकृत उत्तर (नीचे) का VB.NET अनुवाद है। मैंने इसे संक्षिप्तता के लिए थोड़ा परिष्कृत किया है, इस धागे में दूसरों द्वारा दिए गए कुछ सुझावों का संयोजन कर रहा हूं।

मैं मानता हूं कि यह मूल टैग्स (C #) के लिए ऑफ-टॉपिक है, लेकिन जैसा कि मैं इस ठीक समाधान के VB.NET संस्करण को खोजने में सक्षम नहीं था, मुझे लगता है कि अन्य भी दिखेंगे। लैम्ब्डा अनुवाद थोड़ा मुश्किल हो सकता है, इसलिए मैं किसी को परेशानी से बचाना चाहूंगा।

ध्यान दें कि यह विशेष कार्यान्वयन ServiceEndpointरनटाइम पर कॉन्फ़िगर करने की क्षमता प्रदान करता है।


कोड:

Namespace Service
  Public NotInheritable Class Disposable(Of T)
    Public Shared ChannelFactory As New ChannelFactory(Of T)(Service)

    Public Shared Sub Use(Execute As Action(Of T))
      Dim oProxy As IClientChannel

      oProxy = ChannelFactory.CreateChannel

      Try
        Execute(oProxy)
        oProxy.Close()

      Catch
        oProxy.Abort()
        Throw

      End Try
    End Sub



    Public Shared Function Use(Of TResult)(Execute As Func(Of T, TResult)) As TResult
      Dim oProxy As IClientChannel

      oProxy = ChannelFactory.CreateChannel

      Try
        Use = Execute(oProxy)
        oProxy.Close()

      Catch
        oProxy.Abort()
        Throw

      End Try
    End Function



    Public Shared ReadOnly Property Service As ServiceEndpoint
      Get
        Return New ServiceEndpoint(
          ContractDescription.GetContract(
            GetType(T),
            GetType(Action(Of T))),
          New BasicHttpBinding,
          New EndpointAddress(Utils.WcfUri.ToString))
      End Get
    End Property
  End Class
End Namespace

उपयोग:

Public ReadOnly Property Jobs As List(Of Service.Job)
  Get
    Disposable(Of IService).Use(Sub(Client) Jobs = Client.GetJobs(Me.Status))
  End Get
End Property

Public ReadOnly Property Jobs As List(Of Service.Job)
  Get
    Return Disposable(Of IService).Use(Function(Client) Client.GetJobs(Me.Status))
  End Get
End Property

1

हमारे सिस्टम आर्किटेक्चर ने अक्सर क्लाइंट आई के उदाहरण बनाने के लिए यूनिटी आईओसी फ्रेमवर्क का उपयोग किया है ताकि अन्य डेवलपर्स द्वारा उपयोग किए जाने के लिए कोई निश्चित तरीका न हो।using{} ब्लॉक। यथासंभव इसे फुलप्रूफ बनाने के लिए, मैंने इस कस्टम क्लास को बनाया जो क्लाइंटबेस को बढ़ाता है, और डिस्पोजल पर चैनल को बंद करने का काम संभालता है, या किसी को स्पष्ट रूप से बनाए गए एकता के उदाहरण का निपटान नहीं करने की स्थिति में अंतिम रूप देता है।

वहाँ भी सामान है कि कस्टम क्रेडेंशियल्स और सामान के लिए चैनल स्थापित करने के लिए कंस्ट्रक्टर में किए जाने की आवश्यकता है, इसलिए यहां ...

public abstract class PFServer2ServerClientBase<TChannel> : ClientBase<TChannel>, IDisposable where TChannel : class
{
    private bool disposed = false;

    public PFServer2ServerClientBase()
    {
        // Copy information from custom identity into credentials, and other channel setup...
    }

    ~PFServer2ServerClientBase()
    {
        this.Dispose(false);
    }

    void IDisposable.Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    public void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            try
            {
                    if (this.State == CommunicationState.Opened)
                        this.Close();
            }
            finally
            {
                if (this.State == CommunicationState.Faulted)
                    this.Abort();
            }
            this.disposed = true;
        }
    }
}

तो एक ग्राहक बस कर सकते हैं:

internal class TestClient : PFServer2ServerClientBase<ITest>, ITest
{
    public string TestMethod(int value)
    {
        return base.Channel.TestMethod(value);
    }
}

और कॉल करने वाला इनमें से कुछ भी कर सकता है:

public SomeClass
{
    [Dependency]
    public ITest test { get; set; }

    // Not the best, but should still work due to finalizer.
    public string Method1(int value)
    {
        return this.test.TestMethod(value);
    }

    // The good way to do it
    public string Method2(int value)
    {
        using(ITest t = unityContainer.Resolve<ITest>())
        {
            return t.TestMethod(value);
        }
    }
}

आप अपनी
डिस्पोज़

@Chad - मैं Microsoft के सामान्य फ़ाइनल / डिस्पोज़ डिज़ाइन पैटर्न का अनुसरण कर रहा था: msdn.microsoft.com/en-us/library/b1yfkh5e%28VS.71%29.aspx यह सच है कि मैं चर का उपयोग नहीं कर रहा हूँ, क्योंकि मैं डॉन एक सामान्य निपटान और एक अंतिम रूप के बीच कोई अलग सफाई करने की आवश्यकता नहीं है। इसे फिर से लिखा जा सकता है बस अंतिम रूप से कॉल डिस्पोज़ () करें और कोड को डिस्पोज़ (बूल) से डिस्पोज़ () में ले जाएँ।
कोडिंगविथस्पीक

फाइनल ओवरहेड जोड़ते हैं, और नियतात्मक नहीं होते हैं। जब भी संभव हो मैं उनसे बचता हूं। आप प्रतिनिधियों को इंजेक्ट करने के लिए और ब्लॉक का उपयोग करने वालों में डालने के लिए एकता के स्वचालित कारखानों का उपयोग कर सकते हैं, या (बेहतर) एक इंजेक्शन इंटरफ़ेस पर एक विधि के पीछे सेवा व्यवहार को बनाने / कॉल / छिपाने का उपयोग कर सकते हैं। निर्भरता के लिए प्रत्येक कॉल प्रॉक्सी बनाता है, इसे कॉल करता है, और इसका निपटान करता है।
TrueWill

0

मैंने इस पोस्ट पर कुछ उत्तर दिए और अपनी आवश्यकताओं के अनुसार इसे अनुकूलित किया।

मैं इस DoSomethingWithClient()विधि का उपयोग करने से पहले WCF क्लाइंट के साथ कुछ करने की क्षमता चाहता था ।

public interface IServiceClientFactory<T>
{
    T DoSomethingWithClient();
}
public partial class ServiceClient : IServiceClientFactory<ServiceClient>
{
    public ServiceClient DoSomethingWithClient()
    {
        var client = this;
        // do somthing here as set client credentials, etc.
        //client.ClientCredentials = ... ;
        return client;
    }
}

यहाँ सहायक वर्ग है:

public static class Service<TClient>
    where TClient : class, ICommunicationObject, IServiceClientFactory<TClient>, new()
{
    public static TReturn Use<TReturn>(Func<TClient, TReturn> codeBlock)
    {
        TClient client = default(TClient);
        bool success = false;
        try
        {
            client = new TClient().DoSomethingWithClient();
            TReturn result = codeBlock(client);
            client.Close();
            success = true;
            return result;
        }
        finally
        {
            if (!success && client != null)
            {
                client.Abort();
            }
        }
    }
}

और मैं इसका उपयोग कर सकता हूं:

string data = Service<ServiceClient>.Use(x => x.GetData(7));

बाइंडिंग और एंडपोइंग का उपयोग करके ग्राहक निर्माणकर्ता के बारे में क्या है? TClient (बाइंडिंग, एंडपोइंग)
किकेनेट

0

एक चैनल के लिए मेरा अपना रैपर है जो इस प्रकार लागू होता है:

public void Dispose()
{
        try
        {
            if (channel.State == CommunicationState.Faulted)
            {
                channel.Abort();
            }
            else
            {
                channel.Close();
            }
        }
        catch (CommunicationException)
        {
            channel.Abort();
        }
        catch (TimeoutException)
        {
            channel.Abort();
        }
        catch (Exception)
        {
            channel.Abort();
            throw;
        }
}

यह अच्छी तरह से काम करता है और उपयोग करने वाले ब्लॉक का उपयोग करने की अनुमति देता है।


0

निम्न सहायक कॉल करने की अनुमति देता है voidऔर गैर-शून्य तरीके। उपयोग:

var calculator = new WcfInvoker<CalculatorClient>(() => new CalculatorClient());
var sum = calculator.Invoke(c => c.Sum(42, 42));
calculator.Invoke(c => c.RebootComputer());

वर्ग ही है:

public class WcfInvoker<TService>
    where TService : ICommunicationObject
{
    readonly Func<TService> _clientFactory;

    public WcfInvoker(Func<TService> clientFactory)
    {
        _clientFactory = clientFactory;
    }

    public T Invoke<T>(Func<TService, T> action)
    {
        var client = _clientFactory();
        try
        {
            var result = action(client);
            client.Close();
            return result;
        }
        catch
        {
            client.Abort();
            throw;
        }
    }

    public void Invoke(Action<TService> action)
    {
        Invoke<object>(client =>
        {
            action(client);
            return null;
        });
    }
}

0

क्लाइंटबेस के आधार पर एक प्रॉक्सी वर्ग उत्पन्न करने की आवश्यकता के बिना चैनल के निर्माण और कैशिंग को प्रबंधित करने की आवश्यकता के बिना ग्राहक के निपटान () को ओवरराइड करें ! (ध्यान दें कि WcfClient एक ABSTRACT वर्ग नहीं है और क्लाइंटबेस पर आधारित है)

// No need for a generated proxy class
//using (WcfClient<IOrderService> orderService = new WcfClient<IOrderService>())
//{
//    results = orderService.GetProxy().PlaceOrder(input);
//}

public class WcfClient<TService> : ClientBase<TService>, IDisposable
    where TService : class
{
    public WcfClient()
    {
    }

    public WcfClient(string endpointConfigurationName) :
        base(endpointConfigurationName)
    {
    }

    public WcfClient(string endpointConfigurationName, string remoteAddress) :
        base(endpointConfigurationName, remoteAddress)
    {
    }

    public WcfClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
        base(endpointConfigurationName, remoteAddress)
    {
    }

    public WcfClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
        base(binding, remoteAddress)
    {
    }

    protected virtual void OnDispose()
    {
        bool success = false;

        if ((base.Channel as IClientChannel) != null)
        {
            try
            {
                if ((base.Channel as IClientChannel).State != CommunicationState.Faulted)
                {
                    (base.Channel as IClientChannel).Close();
                    success = true;
                }
            }
            finally
            {
                if (!success)
                {
                    (base.Channel as IClientChannel).Abort();
                }
            }
        }
    }

    public TService GetProxy()
    {
        return this.Channel as TService;
    }

    public void Dispose()
    {
        OnDispose();
    }
}

0

ऐसा करने का मेरा तरीका विरासत में मिला वर्ग बनाने के लिए है जो स्पष्ट रूप से आईडीसोपायरी लागू करता है। यह उन लोगों के लिए उपयोगी है जो सेवा संदर्भ (सेवा जोड़ें संदर्भ) जोड़ने के लिए गुई का उपयोग करते हैं। मैं इस श्रेणी को सेवा संदर्भ बनाते हुए प्रोजेक्ट में छोड़ देता हूं और डिफ़ॉल्ट क्लाइंट के बजाय इसका उपयोग करता हूं:

using System;
using System.ServiceModel;
using MyApp.MyService; // The name you gave the service namespace

namespace MyApp.Helpers.Services
{
    public class MyServiceClientSafe : MyServiceClient, IDisposable
    {
        void IDisposable.Dispose()
        {
            if (State == CommunicationState.Faulted)
            {
                Abort();
            }
            else if (State != CommunicationState.Closed)
            {
                Close();
            }

            // Further error checks and disposal logic as desired..
        }
    }
}

नोट: यह डिस्पोजल का सिर्फ एक सरल कार्यान्वयन है, यदि आप चाहें तो अधिक जटिल डिस्पोजल तर्क को लागू कर सकते हैं।

फिर आप अपने सभी कॉल को नियमित सेवा क्लाइंट के साथ सुरक्षित क्लाइंट के साथ कर सकते हैं, जैसे:

using (MyServiceClientSafe client = new MyServiceClientSafe())
{
    var result = client.MyServiceMethod();
}

मुझे यह समाधान पसंद है क्योंकि इसके लिए मुझे इंटरफ़ेस परिभाषाओं तक पहुंच की आवश्यकता नहीं है और मैं usingकथन का उपयोग कर सकता हूं क्योंकि मैं अपेक्षा करता हूं कि मेरे कोड को कम या ज्यादा देखने की अनुमति दी जाए।

आपको अभी भी उन अपवादों को संभालने की आवश्यकता होगी जिन्हें इस थ्रेड में अन्य टिप्पणियों में बताया गया है।


-2

आप विधि DynamicProxyका विस्तार करने के लिए भी उपयोग कर सकते हैं Dispose()। इस तरह आप कुछ कर सकते हैं:

using (var wrapperdProxy = new Proxy<yourProxy>())
{
   // Do whatever and dispose of Proxy<yourProxy> will be called and work properly.
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.