WCF - inheriting from a common interface
At work I have a few dozen (and will at some point get to hundreds) workflows with the same structure:
I have long wanted to refactor all the workflows to use a common interface, instead of copy-pasting the same code all over the place. Today, after spending 3 days on this, I finally made it :) so I'm saving it here for reference.
This is the common implementation of the above interface:
This is the client used to call the workflow:
This is how I call the workflow, using the address retrieved from
Finally, these are the helper functions (in the tools static class):
Well... hope this helps anyone else who has this idea :)
- [XmlSerializerFormat]
- [ServiceContract(Namespace = "http://schema.company.com/messages/")]
- public interface IBasicContract<TRequest, TResponse>
- where TRequest : class
- where TResponse : class
- {
- [XmlSerializerFormat]
- [OperationContract(Name = "GetReport",
- Action = "http://schema.company.com/messages/GetReport",
- ReplyAction = "http://schema.company.com/messages/GetReportResponse")]
- [ServiceKnownType(typeof (Aggregate))]
- TResponse GetReport(TRequest inquiry);
- [XmlSerializerFormat]
- [OperationContract(Name = "GetRawReport",
- Action = "http://schema.company.com/messages/GetRawReport",
- ReplyAction = "http://schema.company.com/messages/GetRawReportResponse")]
- string GetRawReport(string guid);
- [XmlSerializerFormat]
- [OperationContract(Name = "GetArchiveReport",
- Action = "http://schema.company.com/messages/GetArchiveReport",
- ReplyAction = "http://schema.company.com/messages/GetArchiveReportResponse")]
- [ServiceKnownType(typeof (Aggregate))]
- TResponse GetArchiveReport(string guid);
- }
I have long wanted to refactor all the workflows to use a common interface, instead of copy-pasting the same code all over the place. Today, after spending 3 days on this, I finally made it :) so I'm saving it here for reference.
This is the common implementation of the above interface:
- [ServiceBehavior(Namespace = "http://schema.company.com/messages/")]
- public abstract class BasicWorkflowSvc<TRequest, TResponse, TWorkflow> : IBasicContract<TRequest, TResponse>
- where TRequest : class
- where TResponse : class
- where TWorkflow : class
- {
- public virtual TResponse GetReport(TRequest inquiry)
- {
- TResponse res = null;
- using (var wr = new WorkflowRuntime())
- {
- var waitHandle = new AutoResetEvent(false);
- wr.WorkflowCompleted +=
- delegate(object sender, WorkflowCompletedEventArgs e)
- {
- res = (TResponse) e.OutputParameters["wfOutput"];
- waitHandle.Set();
- };
- var arguments = new Dictionary<string, object>();
- arguments.Add("wfInput", inquiry);
- var wi = wr.CreateWorkflow(typeof (TWorkflow), arguments);
- wi.Start();
- waitHandle.WaitOne();
- }
- return res;
- }
- public virtual string GetRawReport(string guid)
- {
- using (var db = new DBAccess())
- return db.LoadGUID(guid);
- }
- public abstract TResponse GetArchiveReport(string guid);
- }
This is the client used to call the workflow:
- public class BasicSvcClient<TRequest, TResponse> : ClientBase<IBasicContract<TRequest, TResponse>>, IBasicContract<TRequest, TResponse>
- where TRequest : class
- where TResponse : class
- {
- public BasicSvcClient()
- {
- }
- public BasicSvcClient(string endpointConfigurationName) :
- base(endpointConfigurationName)
- {
- }
- public BasicSvcClient(string endpointConfigurationName, string remoteAddress) :
- base(endpointConfigurationName, remoteAddress)
- {
- }
- public BasicSvcClient(string endpointConfigurationName, EndpointAddress remoteAddress) :
- base(endpointConfigurationName, remoteAddress)
- {
- }
- public BasicSvcClient(Binding binding, EndpointAddress remoteAddress) :
- base(binding, remoteAddress)
- {
- }
- public TResponse GetReport(TRequest inquiry)
- {
- return Channel.GetReport(inquiry);
- }
- public string GetRawReport(string guid)
- {
- return Channel.GetRawReport(guid);
- }
- public TResponse GetArchiveReport(string guid)
- {
- return Channel.GetArchiveReport(guid);
- }
- }
This is how I call the workflow, using the address retrieved from
- using (var wf = new BasicSvcClient<ProductRq_Type, ProductRs_Type>(
- new BasicHttpBinding("httpsDataEndpoint"),
- new EndpointAddress(tools.FindEndpointAddress("Product.IProductSvc"))))
- {
- txtConfirmation.Text = wf.GetReport(request).AsXML();
- }
Finally, these are the helper functions (in the tools static class):
- public static ChannelEndpointElement FindEndpoint(string endpointName)
- {
- var cf = (ClientSection) ConfigurationManager.GetSection("system.serviceModel/client");
- foreach (ChannelEndpointElement endpoint in cf.Endpoints)
- if (endpoint.Name == endpointName)
- return endpoint;
- return null;
- }
- public static string FindEndpointAddress(string endpointName)
- {
- var endpoint = FindEndpoint(endpointName);
- return endpoint == null ? null : endpoint.Address.ToString();
- }
Well... hope this helps anyone else who has this idea :)
Comments
I find this post very helpful, as I ran into a similar situation of duplication across multiple ServiceContract interfaces.