Adding Dynamic methods to a WCF Service
Someone asked me if it’s possible to add a Ping method to every service contract without repeatedly defining it in each service contract? The answer is yes & and it’s fairly simply too.
Here I have a LoginService, which require that implicit Ping method (DateTime Ping(){})like all other service.
[ServiceContract]
class LoginService
{
[OperationContract]
public bool Login(string userName, string password)
{
return true;
}
}
This is how you would configure your host for these implicit methods.
class Program
{
static void Main(string[] args)
{
var sh = new ServiceHost(typeof(LoginService), new Uri("http://localhost:9001"));
GeneratePingMethod(sh);
sh.Open();
Console.ReadLine();
sh.Close();
}
}
Please note “GeneratePingMethod” adds the Ping method to every endpoint. I’m simply creating the operation using the WCF description API and adding it into the description tree. PingImplementationBehavior plugs in a custom OperationInvoker which calls the actual Ping implementation.
private static void GeneratePingMethod(ServiceHost sh)
{
foreach (var endpoint in sh.Description.Endpoints)
{
var cd = endpoint.Contract;
var od = new OperationDescription("Ping", cd);
var inputMsg = new MessageDescription(cd.Namespace + cd.Name + "/Ping", MessageDirection.Input);
var outputMsg = new MessageDescription(cd.Namespace + cd.Name + "/PingResponse", MessageDirection.Output);
var retVal = new MessagePartDescription("PingResult", cd.Namespace); ;
retVal.Type = typeof(DateTime);
outputMsg.Body.ReturnValue = retVal;
od.Messages.Add(inputMsg);
od.Messages.Add(outputMsg);
od.Behaviors.Add(new DataContractSerializerOperationBehavior(od));
od.Behaviors.Add(new PingImplementationBehavior());
cd.Operations.Add(od);
}
}
class PingImplementationBehavior : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{ }
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{ }
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new PingInvoker();
}
public void Validate(OperationDescription operationDescription)
{ }
}
Again a fairly simple invoker which is tightly coupled to the signature of Ping method.
class PingInvoker : IOperationInvoker
{
public object[] AllocateInputs()
{
return new object[0];
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
outputs = new object[0];
return Ping();
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
throw new NotImplementedException();
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
throw new NotImplementedException();
}
public bool IsSynchronous
{
get { return true; }
}
public static DateTime Ping()
{
return DateTime.Now;
}
}