gotnull
gotnull

Reputation: 27214

Modify existing WCF communication object

This is how I used to make method calls:

SvcHelper.Using<SomeWebServiceClient>(proxy =>
{
   proxy.SomeMethod();
}

public class SvcHelper
{
   public static void Using<TClient>(Action<TClient> action) where TClient : ICommunicationObject, IDisposable, new()
   {
   }
}

This is how I make method calls:

ChannelFactory<ISomethingWebService> cnFactory = new ChannelFactory<ISomethingWebService>("SomethingWebService");
ISomethingWebService client = cnFactory.CreateChannel();

using (new OperationContextScope((IContextChannel)client))
{
   client.SomeMethod();
}

My question is: Instead of replacing every instance of my original method call approach; Is there a way to modify my SvcHelper and do the creation of the channel in the SvcHelper constructor and then simply pass the interface like the following:

SvcHelper.Using<ISomethingWebService>(client =>
{
   client.SomeMethod();
}

Hope this makes sense and thanks in advance.

Upvotes: 0

Views: 4092

Answers (2)

Drew Marsh
Drew Marsh

Reputation: 33379

First, you don't want to create a new ChannelFactory<T> every call to the Using helper method. They are the most costly thing to construct in the WCF universe. So, at bare minimum, you will want to use a caching approach there.

Second, you don't want to tie yourself to "client" types at all anymore. Just work straight with the service contract interfaces.

Starting from what you've got, here's where I'd go based on how I've done this in the past:

public class SvcHelper
{
    private static ConcurrentDictionary<ChannelFactoryCacheKey, ChannelFactory> ChannelFactories = new ConcurrentDictionary<ChannelFactoryCacheKey, ChannelFactory>();

    public static void Using<TServiceContract>(Action<TServiceContract> action) where TServiceContract : class
    {
        SvcHelper.Using<TServiceContract>(action, "*");
    }

    public static void Using<TServiceContract>(Action<TServiceContract> action, string endpointConfigurationName) where TServiceContract : class
    {
        ChannelFactoryCacheKey cacheKey = new ChannelFactoryCacheKey(typeof(TServiceContract), endpointConfigurationName);

        ChannelFactory<TServiceContract> channelFactory = (ChannelFactory<TServiceContract>)SvcHelper.ChannelFactories.GetOrAdd(
                                                                                                                                cacheKey,
                                                                                                                                missingCacheKey => new ChannelFactory<TServiceContract>(missingCacheKey.EndpointConfigurationName));

        TServiceContract typedChannel = channelFactory.CreateChannel();
        IClientChannel clientChannel = (IClientChannel)typedChannel;

        try
        {
            using(new OperationContextScope((IContextChannel)typedChannel))
            {
                action(typedChannel);
            }
        }
        finally
        {
            try
            {
                clientChannel.Close();
            }
            catch
            {
                clientChannel.Abort();
            }
        }

    }

    private sealed class ChannelFactoryCacheKey : IEquatable<ChannelFactoryCacheKey>
    {
        public ChannelFactoryCacheKey(Type channelType, string endpointConfigurationName)
        {
            this.channelType = channelType;
            this.endpointConfigurationName = endpointConfigurationName;
        }

        private Type channelType;

        public Type ChannelType
        {
            get
            {
                return this.channelType;
            }
        }

        private string endpointConfigurationName;

        public string EndpointConfigurationName
        {
            get
            {
                return this.endpointConfigurationName;
            }
        }

        public bool Equals(ChannelFactoryCacheKey compareTo)
        {
            return object.ReferenceEquals(this, compareTo)
                       ||
                   (compareTo != null
                       &&
                   this.channelType == compareTo.channelType
                       &&
                   this.endpointConfigurationName == compareTo.endpointConfigurationName);
        }

        public override bool Equals(object compareTo)
        {
            return this.Equals(compareTo as ChannelFactoryCacheKey);
        }

        public override int GetHashCode()
        {
            return this.channelType.GetHashCode() ^ this.endpointConfigurationName.GetHashCode();
        }
    }
} 

Upvotes: 1

Dmitry Reznik
Dmitry Reznik

Reputation: 6862

This should work:

public class SvcHelper
{
    public static void Using<TClient>(Action<TClient> action) where TClient : ICommunicationObject, IDisposable
    {
        ChannelFactory<TClient> cnFactory = new ChannelFactory<TClient>("SomethingWebService");
        TClient client = cnFactory.CreateChannel();
        using (new OperationContextScope((IContextChannel)client))
        {
            action(client);
        }
    }
}

Upvotes: 1

Related Questions