Normally, there are a few steps we have to follow for creating or consuming a WCF service.
- Server side - create a new ServiceHost for new WCF service.
- Client side - make sure the correct client name is setup in client source.
- Make sure - make sure that the .config files of both client/server side are set up properly.
Any spelling error could waste a couple of hours of investigation. I was really really tired of this job until I found some code on stackoverflow recently. Unfortunately, I could not find that thread again. Here I have attached my code instead.
- protected Dictionary<Type, object> GetBehaviors(string name)
- {
- if (!_behaviorCache.ContainsKey(name))
- {
- BehaviorsSection behaviorData = ConfigurationManager.GetSection("system.serviceModel/behaviors") as BehaviorsSection;
- List<BehaviorExtensionElement> behaviors = new List<BehaviorExtensionElement>();
-
- if (behaviorData.ServiceBehaviors.ContainsKey(name))
- {
- behaviorData.ServiceBehaviors[name].All(e =>
- {
- behaviors.Add(e);
- return true;
- });
- }
-
- if (behaviorData.EndpointBehaviors.ContainsKey(name))
- {
- behaviorData.EndpointBehaviors[name].All(e =>
- {
- behaviors.Add(e);
- return true;
- });
- }
-
- _behaviorCache.Add(name, behaviors.ToDictionary(e => e.BehaviorType, e => createBehavior(e)));
- }
-
- return _behaviorCache[name];
- }
-
- private object createBehavior(BehaviorExtensionElement element)
- {
- return element.GetType().GetMethod("CreateBehavior", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic)
- .Invoke(element, new object[0] { });
- }
What is it? It creates a list of behaviors by given behavior name, so a ServiceHost could be created as below.
- private ServiceHost getServiceHost(Type serviceType, Type interfaceType)
- {
- WebServiceAttribute attribute = interfaceType.GetCustomAttribute<WebServiceAttribute>();
-
- if (attribute == null)
- throw new ArgumentException("WebService attribute is missing");
-
- string serviceUrl = GetServiceUrl(attribute, "localhost");
- ServiceHost serviceHost = new ServiceHost(serviceType, new Uri(serviceUrl));
- var serviceMetadataBehavior = new ServiceMetadataBehavior();
- serviceHost.Description.Behaviors.Add(serviceMetadataBehavior);
-
-
- Dictionary<Type, object> behaviors = GetBehaviors(attribute.BehaviorConfiguration);
- behaviors.All(e =>
- {
- serviceHost.Description.Behaviors.Remove(e.Key);
- serviceHost.Description.Behaviors.Add(e.Value as IServiceBehavior);
- return true;
- });
-
- serviceHost.AddServiceEndpoint(interfaceType, GetBinding(attribute), serviceUrl);
- return serviceHost;
- }
If all the information could be fetched from the given attribute, is it enough for service creation? Yes!
- public class WebServiceAttribute : Attribute
- {
- public string RelativePath { get; set; }
- public Type BaseBinding { get; set; }
- public string CustomBinding { get; set; }
- public string BehaviorConfiguration { get; set; }
- public bool UseCallback { get; set; }
-
- private static readonly string DEFAULT_BEHAVIOR = "customBehavior";
- private static readonly string DEFAULT_BINDING = "customWsHttpBinding";
-
- public WebServiceAttribute()
- {
- UseCallback = false;
- BaseBinding = typeof(WSHttpBinding);
- CustomBinding = DEFAULT_BINDING;
- BehaviorConfiguration = DEFAULT_BEHAVIOR;
- }
- }
So, how do we use it? Just apply it on web interface, that's all.
- [ServiceContract]
- [WebService(RelativePath = "/myapp/SimpleService/")]
- public interface ISimpleService
- {
- [OperationContract]
- void Speak(string words);
- }
So, with the help of WebServiceAttribute and the above codes, we are able to create a Service Host via .NET Reflection. Also, it is also possible to create a WCF Service client since this attribute contains all the information for ChannelFactory<> creation.
In the attached sample solution (by VS2015), you can find that the class WebServiceServerProber is used to find all available WCF services in a particular module and create ServiceHost for them; while the WebServiceSingleClientProber class is used in the client to create ChannelFactory.
We have saved hundreds of lines in .config file in our project. I hope it will help you! Let me know if you have any questions.