What is the best workaround for the WCF client `using` block issue?

前端 未结 26 1600
余生分开走
余生分开走 2020-11-22 00:03

I like instantiating my WCF service clients within a using block as it\'s pretty much the standard way to use resources that implement IDisposable:

相关标签:
26条回答
  • 2020-11-22 00:10

    The following helper allows to call void and non-void methods. Usage:

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

    The class itself is:

    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 讨论(0)
  • 2020-11-22 00:10

    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();
        }
    }
    
    0 讨论(0)
  • 2020-11-22 00:11
    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); 
    }); 
    
    0 讨论(0)
  • 2020-11-22 00:13

    I like this way of closing connection:

    var client = new ProxyClient();
    try
    {
        ...
        client.Close();
    }
    finally
    {
        if(client.State != CommunicationState.Closed)
            client.Abort();
    }
    
    0 讨论(0)
  • 2020-11-22 00:15

    I used Castle dynamic proxy to solve the Dispose() issue, and also implemented auto-refreshing the channel when it is in an unusable state. To use this you must create a new interface that inherits your service contract and IDisposable. The dynamic proxy implements this interface and wraps a WCF channel:

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

    I like this since you can inject WCF services without consumers needing to worry about any details of WCF. And there's no added cruft like the other solutions.

    Have a look at the code, it's actually pretty simple: WCF Dynamic Proxy

    0 讨论(0)
  • 2020-11-22 00:15

    Use an extension method:

    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;
        }
    }
    
    0 讨论(0)
提交回复
热议问题