WCF在不断的进步,在4.0下可以很简单的实现无配置WCF,从此不再为哪大段大段的配置而感到头痛了。
但是,现实是残酷的,项目的.net框架版本不是说变就变的。不过,对于会写代码的人来说,总有办法能绕过这些杂七杂八的麻烦事情。
之前的一片文章中已经写了无配置客户端如何实现,但是里面的服务端还是需要配置的,这样还是有少许的不便。所以,今天再来一个完全不用配置文件的WCF测试全套方案不漏——WCF无配置Service端(3.5框架)
首先,来想一下,WCF需要配置的罪魁祸首是谁?微软,呵呵,确实没错,但是,再具体一点——ServiceHost类。
就是它要去读取配置,所以,要想实现无配置的WCF服务端,必须从这个类开刀。于是需要修改一下这个类型的某些行为:
1: public class SimpleServiceHost
2: : ServiceHost
3: {
4:
5: public Uri Uri { get; set; }
6: public Binding Binding { get; private set; }
7:
8: public SimpleServiceHost(Type serviceType,
9: Binding binding, Uri uri)
10: {
11: if (uri == null)
12: throw new ArgumentNullException("uri");
13: if (binding == null)
14: throw new ArgumentNullException("binding");
15: Uri = uri;
16: Binding = binding;
17: InitializeDescription(serviceType, new UriSchemeKeyedCollection(uri));
18: }
19:
20: public SimpleServiceHost(object singletonInstance,
21: Binding binding, Uri uri)
22: {
23: if (singletonInstance == null)
24: throw new ArgumentNullException("singletonInstance");
25: if (uri == null)
26: throw new ArgumentNullException("uri");
27: if (binding == null)
28: throw new ArgumentNullException("binding");
29: Uri = uri;
30: Binding = binding;
31: InitializeDescription(singletonInstance, new UriSchemeKeyedCollection(uri));
32: }
33:
34: protected override ServiceDescription CreateDescription(
35: out IDictionary<string, ContractDescription> implementedContracts)
36: {
37: var result = base.CreateDescription(out implementedContracts);
38: foreach (var contract in implementedContracts.Values)
39: {
40: var endpoint = new ServiceEndpoint(contract, Binding, new EndpointAddress(Uri.ToString()));
41: endpoint.Name = Binding.Name + "_" + contract.Name;
42: result.Endpoints.Add(endpoint);
43: }
44: return result;
45: }
46:
47: }
这个SimpleServiceHost直接继承ServiceHost,然后修改了CreateDescription方法,为契约们增加终结点。
看到这里,一定会想然后哪?
不幸的是,这些就是全部的代码了,后面的都是测试代码了,是不是感觉太简单了,就像被忽悠了。是不是被忽悠就直接看测试吧(别忘了上一片文章里面的两个扩展方法):
1: [TestMethod]
2: public void TestHelloService()
3: {
4: var uri = new Uri("net.tcp://127.0.0.1:2323/Console/");
5: var binding = new NetTcpBinding();
6: new SimpleServiceHost(typeof(HelloService), binding, uri).RunWcfService(
7: () => uri.InvokeWcfClient<IHelloService>(binding,
8: c => Assert.AreEqual("Hello world!", c.Hello("world"))));
9: }
10:
11: [ServiceContract]
12: public interface IHelloService
13: {
14: [OperationContract]
15: string Hello(string name);
16: }
17:
18: public class HelloService
19: : IHelloService
20: {
21: public string Hello(string name)
22: {
23: return "Hello " + name + "!";
24: }
25: }
只要这些测试代码,我们的测试就可以跑了。
如果是实现类实现了多个服务契约会怎么样哪?
1: [TestMethod]
2: public void TestMultiService()
3: {
4: var uri = new Uri("net.tcp://127.0.0.1:2323/Multi/");
5: var binding = new NetTcpBinding();
6: new SimpleServiceHost(typeof(MultiService), binding, uri).RunWcfService(
7: () =>
8: {
9: uri.InvokeWcfClient<IHelloService>(binding,
10: c => Assert.AreEqual("Hello world!", c.Hello("world")));
11: uri.InvokeWcfClient<IEchoService>(binding,
12: c => Assert.AreEqual("abc", c.Echo("abc")));
13: });
14: }
15:
16: [ServiceContract]
17: public interface IEchoService
18: {
19: [OperationContract]
20: string Echo(string message);
21: }
22:
23: public class MultiService
24: : IHelloService, IEchoService
25: {
26: public string Hello(string name)
27: {
28: return "Hello " + name + "!";
29: }
30:
31: public string Echo(string message)
32: {
33: return message;
34: }
35: }
IHelloService的契约不变,再加个IEchoService,在测试用例中同时测试这两个客户端,可以发现依然正常。
有了这个类,在单元测试中,就完全可以抛弃WCF的配置文件(正式的运行环境通常还是需要配置在灵活性方面的优势)。