Hi, I wish to start to post something about WCF, some useful content, someone could find my-blogged-practice trivial or something bad, but first I like to share contents, second this is my “little” way I found useful in my daily job.
This time I wish to talk about Factories, exactly the factory I use to instantiate WCF services in clients. First let me know I don’t really like VisualStudio generated proxies, I don’t like the concept behind a generated proxy like this, my problem began with ASMX generated proxies, and now is still alive with WCF proxies.
I like to have, or to possibly-have, control over intantiation of client proxyes and more, maybe services on server side, many situations I found I needed to have some kind of control on that, and I love WCF for all of the features and extensibility offered. Maybe could be necessary to inject policies, change behaviors, work with addressing, or even encapsulate the features normally written in configuration, this it’s possible with simple and little factory I merely named ServiceFactory.
Now, let begin with another concept, It’s clear that, if one can or would use a factory to create service clients, it’s clear that contracts are shared by a shared assembly, so it would be useful to have a generic ServiceFactory, this could be extended by other features, but focus on simplicity, the factory is quite simple:
First we start create the class defiition, now I will use the simplest static-methods one, but I don’t really like it, I prefer the Singleton kind:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.ServiceModel;
6:
7: namespace XGuy.Common.Services
8: {
9: /// <summary>
10: /// Represent the Factorym used to create service
11: /// clients proxy transparently
12: /// </summary>
13: public class ServiceFactory
14: {
Next, there are essently 3 methods could be used to create proxy services:
1: /// <summary>
2: /// Create a new service proxy based on service
3: /// endpoint interface
4: /// </summary>
5: /// <typeparam name="T">
6: /// the service-interface to use
7: /// </typeparam>
8: /// <returns>an interface transparent proxy</returns>
9: public static T CreateClient<T>() where T : class
10: {
11: Type contract = typeof(T);
12: return CreateClient<T>(contract.Name);
13: }
This first method can be used direct on an inetrface type
1: using System;
2: using System.Collections;
3: using XGuy.Common.Services;
4:
5: namespace XGuy.Testbench
6: {
7: class Program
8: {
9: public static void Main()
10: {
11: ITestService service =
12: ServiceFactory.CreateClient<ITestService>();
13: service.DoSomething();
14: }
15: }
16: }
The second and third method can be used in the same manner, but woh named configured services:
1: /// <summary>
2: /// Create a new service proxy based on service
3: /// endpoint interface by name
4: /// </summary>
5: /// <typeparam name="T">the service-interface to use</typeparam>
6: /// <param name="serviceName">
7: /// the named configured service reference to use
8: /// </param>
9: /// <returns>an interface transparent proxy</returns>
10: public static T CreateClient<T>(Enum serviceName) where T : class
11: {
12: return CreateClient<T>(serviceName.ToString());
13: }
14:
15: /// <summary>
16: /// Create a new service proxy based on service
17: /// endpoint interface by name
18: /// </summary>
19: /// <typeparam name="T">the service-interface to use</typeparam>
20: /// <param name="serviceName">
21: /// the named configured service reference to use
22: /// </param>
23: /// <returns>an interface transparent proxy</returns>
24: public static T CreateClient<T>(string serviceName) where T : class
25: {
26: //get the 'default' value based on type (this could be normally a null)
27: T result = default(T);
28: //obtain the 'real' interface type reference
29: Type contract = typeof(T);
30:
31: ChannelFactory<T> channelFactory = new ChannelFactory<T>(serviceName);
32: result = channelFactory.CreateChannel();
33:
34: ((IClientChannel)result).Open();
35:
36: return result;
37: }
38: }
39: }
Now, this kind of methods are useful in situations where one has a well-know service, configured by arbitrary name and it would be usefull to configure by that, so the method will help so. But now let focus on the first method, someone can see I use an “Enum” parameter, this is just an helper way, because first of all I don’t like literals STRING in code, ok, it’s cler that somethime are obvious to use, but normally I try to avoid, so, I like to use Enumerations as names, because them are well-know “literals” and are something like “real-piece-of-code”. I could agree with someone who can say this is just a strange way to think, but I like it. so the usage could be as this:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using XGuy.Common.Services;
6: using XGuy.Common.Contracts;
7:
8: namespace XGuy.TestConsole
9: {
10: public enum Services
11: {
12: ITestService,
13: IDataransferService,
14: SimpleDataTransfer,
15: FullDataTransfer
16: }
17:
18: class Program
19: {
20: static void Main(string[] args)
21: {
22: Services srvType = Services.SimpleDataTransfer;
23: //get a client proxy with simplest configuration
24: IDataTransferService simple = ServiceFactory
25: .CreateClient<IDataTransferService>(srvType);
26:
27: Services srvType = Services.FullDataTransfer;
28: //get a client proxy with full configuration
29: IDataTransferService full = ServiceFactory
30: .CreateClient<IDataTransferService>(srvType);
31: }
32: }
33: }
In the above example I created a console, with one enumeration named Services, so I assume I can have two DataTranferService configurations, one Simple, with some configuration like BasicHttpBinding, and more, and one Full maybe with full security and NetTcpBinding enable:
1: <?xml version="1.0" encoding="utf-8" ?>
2: <configuration>
3: <system.serviceModel>
4: <client>
5: <endpoint
6: name="FulldDataTransfer"
7: binding="netTcpBinding"
8: bindingConfiguration="Bindings.Full"
9: contract="XGuy.Services.IDataTransferService"
10: address="http://myServer/myServices/DataTransferService.svc"/>
11: <endpoint
12: name="SimpleDataTransfer"
13: binding="basicHttpBinding"
14: bindingConfiguration="Bindings.Default"
15: contract="XGuy.Services.IDataTransferService"
16: address="http://myServer/myServices/DataTransferService.svc"/>
17: </client>
18: <bindings>
19: <basicHttpBinding>
20: <binding name="Bindings.Default"
21: maxReceivedMessageSize="800000000">
22: <readerQuotas maxStringContentLength="65536"/>
23: </binding>
24: </basicHttpBinding>
25: <netTcpBinding>
26: <binding name="Bindings.Full">
27: <security mode="Transport" >
28: <transport clientCredentialType="Windows" />
29: </security>
30: </binding>
31: </netTcpBinding>
32: </bindings>
33: </system.serviceModel>
34: </configuration>
Enjoy! Ciao!