You were close. Firstly, you shouldn't change that Reference.vb
file since it will be overwritten when the service definition gets updated and changing that file is not a good practice.
What you can do instead is using Proxy Pattern. The Proxy will be used to call the methods in services and manage the connection state etc. I will write in C# since I don't know VB but you'll get the idea. I will simplify this as much as I can.
The interface of Proxy class might look like this:
public interface IProxy
{
/// <summary>
/// Execute call to service method
/// </summary>
/// <typeparam name="TBusiness">Service interface</typeparam>
/// <typeparam name="TResult">Service method return type</typeparam>
/// <param name="call">Service method</param>
/// <returns>Returns what service method returns</returns>
TResult Execute<TBusiness, TResult>(Func<TBusiness, TResult> call) where TBusiness : class;
/// <summary>
/// Execute call to void service method
/// </summary>
/// <typeparam name="TBusiness">Service Interface</typeparam>
/// <param name="call">Service method</param>
void Execute<TBusiness>(Action<TBusiness> call) where TBusiness : class;
}
As you see, there are two methods in this interface. One of them will be used to call a service method which has a return type, the other one will be used for void methods in the service. You can put non-generic version of these methods to the interface too.
The implementation might be like this:
public class ServiceProxy : IProxy
{
protected void ExecuteCall<TContract>(Action<TContract> code) where TContract : class
{
var contractChannel = default(TContract);
try
{
//Create an instance of service client and call the method
contractChannel = Activator.CreateInstance<TContract>();
code(contractChannel);
((ICommunicationObject)contractChannel).Close();
}
catch (FaultException)
{
((ICommunicationObject)contractChannel).Abort();
}
catch (CommunicationException)
{
((ICommunicationObject)contractChannel).Abort();
}
catch (TimeoutException)
{
((ICommunicationObject)contractChannel).Abort();
}
}
public TResult Execute<TContract, TResult>(Func<TContract, TResult> call) where TContract : class
{
return ExecuteCall(call);
}
public void Execute<TContract>(Action<TContract> call) where TContract : class
{
ExecuteCall(call);
}
}
Then, you can use it like this:
var proxy = new ServiceProxy();
proxy.Execute<Service1Client>(a => a.MethodInTheService());
What's great about this approach and how you can make it perfect is:
- You might not want to create the proxy as
new ServiceProxy()
but inject IProxy
as ServiceProxy
and use it as WCF client for now but if it changes to Web API in the future e.g., implement and inject WebApiProxy
then.
- You can use contract interfaces to call the proxy methods.
- You can do whatever you want in the proxy class like caching the channels, getting the service endpoints from the database etc.