c# - डब्ल्यूसीएफ क्लाइंट 'ब्लॉक' मुद्दे का उपयोग करने के लिए सबसे अच्छा कामकाज क्या है?




vb.net wcf (18)

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

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

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

एमएसडीएन लेख में सुझाए गए कामकाज को एक using ब्लॉक 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 पर blog.davidbarret.net । असल में आप क्लाइंट के Dispose() विधि को ओवरराइड करते हैं जहां भी आप इसका इस्तेमाल करते हैं। कुछ इस तरह:

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

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

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


सारांश

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

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

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

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

विवरण

इस प्रकार दिए गए सभी उत्तरों IDisposable के डब्ल्यूसीएफ चैनल कार्यान्वयन में "बग" के आसपास होने की समस्या को दूर करते हैं। ऐसा उत्तर जो सबसे संक्षिप्त प्रोग्रामिंग मॉडल प्रदान करता है (आपको अप्रबंधित संसाधनों पर निपटने के लिए using ब्लॉक 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 ब्लॉक का using करके हमारी सेवा का उपभोग करने की अनुमति देता है:

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

Making this generic

All we have done so far is to reformulate Tomas' solution . What prevents this code from being generic is the fact that ProxyWrapper class has to be re-implemented for every service contract we want. We will now look at a class that allows us to create this type dynamically using 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();
    }
}

With our new helper class we can now write

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

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

Note that you could also use the same technique (with slight modifications) for auto-generated clients inheriting for ClientBase<> (instead of using ChannelFactory<> ), or if you want to use a different implementation of IDisposable to close your channel.


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

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

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

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

ये लौटने वाले चर को आसान बना देंगे।


अंततः मुझे इस समस्या के एक साफ समाधान की ओर कुछ ठोस कदम मिले हैं।

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

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

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


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

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

एक विस्तार विधि का प्रयोग करें:

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

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

यह .NET 4 का उपयोग करता है (विशेष रूप से: contravariance, 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.");
    }
}

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

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" विधि सहायक लिखकर, हम चैनल फैक्ट्री को जिस तरीके से चाहते हैं, उसे कॉन्फ़िगर कर सकते हैं, आदि। हम जेनरेट प्रॉक्सी का उपयोग करने के लिए भी बाध्य नहीं हैं - कोई भी इंटरफ़ेस करेगा ।


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

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

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

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


यह डब्ल्यूसीएफ क्लाइंट कॉल को संभालने के लिए माइक्रोसॉफ्ट की अनुशंसित तरीका है:

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

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

अतिरिक्त जानकारी डब्ल्यूसीएफ पर इतने सारे लोग इस सवाल से पूछ रहे हैं कि माइक्रोसॉफ्ट ने अपवादों को संभालने के तरीके को प्रदर्शित करने के लिए एक समर्पित नमूना भी बनाया है:

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

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

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

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

CommunicationException अपवाद से कई अपवाद प्राप्त हुए हैं और मुझे नहीं लगता कि उन अपवादों में से अधिकांश को पुनः प्रयास किया जाना चाहिए। मैं एमएसडीएन पर प्रत्येक अपवाद के माध्यम से डूब गया और पुनः प्रयास करने योग्य अपवादों (ऊपर 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
}

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


यह क्या है?

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

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

सरल डब्ल्यूसीएफ ग्राहक उपयोग

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

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

    }
}

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


I have my own wrapper for a channel which implements Dispose as follows:

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

This seems to work well and allows a using block to be used.


I have written a simple base class that handles this. It's available as a NuGet package and it's quite easy to use.

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

I referred few answers on this post and customized it as per my needs.

I wanted the ability to do something with WCF client before using it so the DoSomethingWithClient() method.

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

Here is the helper class:

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

And I can use it as:

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

I'd like to add implementation of Service from Marc Gravell's answer for case of using ServiceClient instead of ChannelFactory.

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

Our system architecture often uses the Unity IoC framework to create instances of ClientBase so there's no sure way to enforce that the other developers even use using{} blocks. In order to make it as fool-proof as possible, I made this custom class that extends ClientBase, and handles closing down the channel on dispose, or on finalize in case someone doesn't explicitly dispose of the Unity created instance.

There is also stuff that needed to be done in the constructor to set up the channel for custom credentials and stuff, so that's in here too...

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

Then a client can simply:

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

And the caller can do any of these:

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

Override the client's Dispose() without the need to generate a proxy class based on ClientBase, also without the need to manage channel creation and caching ! (Note that WcfClient is not an ABSTRACT class and is based on ClientBase)

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

You could also use a DynamicProxy to extend the Dispose() method. This way you could do something like:

using (var wrapperdProxy = new Proxy<yourProxy>())
{
   // Do whatever and dispose of Proxy<yourProxy> will be called and work properly.
}

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

So it allows to write return statements nicely:

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




wcf-client