Web service proxies for asmx web services are quite annoying. They are supposed to represent the contract between the service and the client and without any doubt there should be such a contract. But in reality they represent not only the contract between the service and the client but also the tool that generates them. And they make you vulnerable to any inefficiencies of that tool. Do you like to be dependant in such a way?
Well, fortunately WCF offers much better approach. The contract between the service and the client is represented by an interface decorated with the right attributes. That is all you need for the contract between the client and the server. There is no third party involved and everything is clear. I like that.
Here is an example of a wrapper around the WCF client.
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.Text;
public class WebServiceClient<T> : IDisposable
{
private readonly T _channel;
private readonly IClientChannel _clientChannel;
public WebServiceClient(string url) : this(url, null)
{
}
/// <summary>
/// Use action to change some of the connection properties before creating the channel
/// </summary>
public WebServiceClient(string url,
Action<CustomBinding, HttpTransportBindingElement, EndpointAddress, ChannelFactory> init)
{
var binding = new CustomBinding();
binding.Elements.Add(
new TextMessageEncodingBindingElement(MessageVersion.Soap12, Encoding.UTF8));
var transport = url.StartsWith("https", StringComparison.InvariantCultureIgnoreCase)
? new HttpsTransportBindingElement()
: new HttpTransportBindingElement();
binding.Elements.Add(transport);
var address = new EndpointAddress(url);
var factory = new ChannelFactory<T>(binding, address);
if(init != null)
{
init(binding, transport, address, factory);
}
this._clientChannel = (IClientChannel)factory.CreateChannel();
this._channel = (T)this._clientChannel;
}
/// <summary>
/// Use this property to call service methods
/// </summary>
public T Channel
{
get { return this._channel; }
}
/// <summary>
/// Use this porperty when working with
/// Session or Cookies
/// </summary>
public IClientChannel ClientChannel
{
get { return this._clientChannel; }
}
public void Dispose()
{
this._clientChannel.Dispose();
}
}
And the way you use it is just.
var client =
new WebServiceClient<IMyContractInterface>(@"http://www.MySite.com/MyService.asmx");
var result = client.Channel.SayHello("John");
You can use the init action to change some of the connection properties before creating the channel.
For example to increase the MaxReceivedMessageSize you could use:
var client =
new WebServiceClient<IMyContractInterface>(
serviceUrl,
(binding, transport, address, channel) =>
transport.MaxReceivedMessageSize = Int32.MaxValue
);
Your interface contains the signitures of the methods in your service. And a sample attribute decoration for the interface contract could look like that.
using System.ServiceModel;
[XmlSerializerFormat]
[ServiceContract(Namespace = "http://blog.bodurov.com/")]
public interface IMyContractInterface
{
[OperationContract]
string SayHello(string name);
[OperationContract]
BusinessObj ReturnBusinessObject();
}
Here is a sample attribute decoration of a business object BusinessObj on the side of the client returned by the service.
using System.Runtime.Serialization;
[DataContract(Name = "BusinessObj", Namespace = "")]
public struct BusinessObj
{
[DataMember(IsRequired = true)]
public string ID { get; set; }
[DataMember(IsRequired = false)]
public string Name { get; set; }
}
One consideration here is that the code in the constructor of WebServiceClient may be costly in terms of performance. So you probably want to cache it like that:
public class MyObj{
private static WebServiceClient<IMyContractInterface> _client;
public IMyContractInterface Channel{
get{
if(_client == null){
_client = new WebServiceClient<IMyContractInterface>(
@"http://www.MySite.com/MyService.asmx");
}
return _client.Channel;
}
}
public void MyMethod(){
var result1 = this.Channel.SayHello("John");
...
}
}
Or if you want to make it thread safe:
public class MyObj{
private static readonly object _synch = new object();
private static volatile WebServiceClient<IMyContractInterface> _client;
public IMyContractInterface Channel{
get{
if(_client == null){
lock(_synch){
if(_client == null){
_client = new WebServiceClient<IMyContractInterface>(
@"http://www.MySite.com/MyService.asmx");
}
}
}
return _client.Channel;
}
}
public void MyMethod(){
var result1 = this.Channel.SayHello("John");
...
}
}