本方案解决了下面3个主要的问题:
1、减少配置,为了避免每次新增service都需要去修改配置文件,包括服务器端跟各个客户端的。
2、能够使用函数重载,泛型函数,以及泛型类。
3、使项目能够快速地在wcf与直接调用dll之间切换。
整个解决方案分为四块内容:1、客户端,2、契约层,3、服务端,4、实现层
1、客户端:只能看到契约层,而看不到具体的实现;但也可以通过直接引用实现层,从而脱离wcf(需要修改工厂方法,也可以改进一下,通过配置文件来做这个事情,但这个不是本解决方案的重点,有兴趣的可以自己去实现)。
2、契约层:包含DTO对象模型跟各个服务接口
3、服务端:能看到契约层跟实现层
4、实现层:使用Autofac跟Castle.DynamicProxy进行IOC跟AOP(动态代理中的拦截器)
为解决第一个问题,早些年,已有人通过只暴露WCF的一个服务,而向该服务传递类名,函数名,参数数组来解决这一问题,但是用字符串在我看来实在是不太好,谁能保证自己在编码的时候不会把字符串给写错呢,而且万一需要更改服务名,vs的代码重构机制也处理不了字符串。所以我觉得利用接口,应该是最好的解决方案,但问题是谁又能在不知道具体的实现类的情况下,而针对接口凭空实例化出来一个对象呢。答案是有的,用Emit指令,不过Emit指令实在是有点难。此时我又想到了单元测试中常用的Rhino Mocks跟moq框架,便去查看了它们的源代码,我发现其中一个用的.net自带的真实代理(RealProxy),另外个用的是Castle.DynamicProxy,个人比较喜欢Castle.DynamicProxy,就用了它的CreateInterfaceProxyWithoutTarget,然后在自定义的拦截器中去调用实际的WCF服务,将返回回来的值设置给invocation.ReturnValue。
并且针对方法中的传引用的参数,进行了处理,使其能够对传引用的参数进行赋值。
下图为整个解决方案的结构:
测试的接口:
1 public interface IMyTest 2 { 3 int Test1(); 4 void Test2(); 5 int Test3(int value); 6 TestModel Test4(ref TestModel T); 7 void Test5(TestModel T); 8 void Test6(out int value); 9 void Test7(ref int value); 10 int Test8(int value = 8); 11 int Test9(int value1 = 0, int value2 = 0); 12 int Test10(params int[] values); 13 int Test11(ref List<int> lst); 14 int Test12(TestEnumModel m); 15 TestEnumModel Test13(TestEnumModel m); 16 T Test14<T>(T m); 17 string Test14<T>(T m, int value); 18 string Test14<T>(T m, string value); 19 string Test14<T>(T m, TestModel model); 20 }
IMyTest接口
1 public interface IMyTest2<T> 2 { 3 T Test(T aa); 4 }
IMyTest2泛型接口
DTO对象:
1 public class TestModel 2 { 3 public int A { get; set; } 4 } 5 6 public class TestImpModel : TestModel 7 { 8 public string B { get; set; } 9 } 10 11 public enum TestEnumModel 12 { 13 A = 0, 14 B = 1 15 }
DTO对象们
具体实现:
1 internal class MyTest : IMyTest 2 { 3 public int Test1() 4 { 5 return 1; 6 } 7 8 public void Test2() 9 { 10 11 } 12 13 public int Test3(int value) 14 { 15 return value + 1; 16 } 17 18 public TestModel Test4(ref TestModel T) 19 { 20 T.A = T.A + 1; 21 return T; 22 } 23 24 public void Test5(TestModel T) 25 { 26 T.A = T.A + 1; 27 } 28 29 public void Test6(out int value) 30 { 31 value = 3; 32 } 33 34 public void Test7(ref int value) 35 { 36 value++; 37 } 38 39 40 public int Test8(int value = 8) 41 { 42 return value; 43 } 44 45 46 public int Test9(int value1 = 0, int value2 = 0) 47 { 48 return value1 + value2; 49 } 50 51 52 public int Test10(params int[] values) 53 { 54 return values.Sum(); 55 } 56 57 58 public int Test11(ref List<int> lst) 59 { 60 lst.Add(3); 61 return lst.Sum(); 62 } 63 64 public int Test12(TestEnumModel m) 65 { 66 return (int)m; 67 } 68 69 public TestEnumModel Test13(TestEnumModel m) 70 { 71 return m; 72 } 73 74 public T Test14<T>(T m) 75 { 76 return m; 77 } 78 79 public string Test14<T>(T m, int value) 80 { 81 return m.ToString() + value.ToString(); 82 } 83 84 public string Test14<T>(T m, string value) 85 { 86 return m.ToString() + value; 87 } 88 89 public string Test14<T>(T m, TestModel model) 90 { 91 model.A += 1; 92 return m.ToString() + model.A.ToString(); 93 } 94 }
实现了IMyTest的MyTest类
1 internal class MyTest2<T> : IMyTest2<T> 2 { 3 public T Test(T aa) 4 { 5 return aa; 6 } 7 }
实现了IMyTest2的MyTest2泛型类
测试用例:
1 [TestMethod] 2 public void TestMethod1() 3 { 4 Assert.AreEqual(1, MyCreator.Create<IMyTest>().Test1()); 5 } 6 7 [TestMethod] 8 public void TestMethod2() 9 { 10 MyCreator.Create<IMyTest>().Test2(); 11 } 12 13 [TestMethod] 14 public void TestMethod3() 15 { 16 Assert.AreEqual(4, MyCreator.Create<IMyTest>().Test3(3)); 17 Assert.AreEqual(3, MyCreator.Create<IMyTest>().Test3(2)); 18 } 19 20 [TestMethod] 21 public void TestMethod4() 22 { 23 var tm = new TestModel() { A = 2 }; 24 var t = MyCreator.Create<IMyTest>().Test4(ref tm); 25 Assert.AreEqual(3, t.A); 26 Assert.AreEqual(3, tm.A); 27 } 28 29 [TestMethod] 30 public void TestMethod5() 31 { 32 var t = new TestModel() { A = 2 }; 33 MyCreator.Create<IMyTest>().Test5(t); 34 Assert.AreEqual(2, t.A); 35 } 36 37 [TestMethod] 38 public void TestMethod6() 39 { 40 int i = 0; 41 MyCreator.Create<IMyTest>().Test6(out i); 42 Assert.AreEqual(3, i); 43 } 44 45 [TestMethod] 46 public void TestMethod7() 47 { 48 int i = 0; 49 MyCreator.Create<IMyTest>().Test7(ref i); 50 Assert.AreEqual(1, i); 51 } 52 53 [TestMethod] 54 public void TestMethod8() 55 { 56 Assert.AreEqual(8, MyCreator.Create<IMyTest>().Test8()); 57 } 58 59 [TestMethod] 60 public void TestMethod9() 61 { 62 Assert.AreEqual(12, MyCreator.Create<IMyTest>().Test9(value2: 12)); 63 } 64 65 [TestMethod] 66 public void TestMethod10() 67 { 68 Assert.AreEqual(3, MyCreator.Create<IMyTest>().Test10(1, 2)); 69 } 70 71 [TestMethod] 72 public void TestMethod11() 73 { 74 List<int> lst = new List<int>(); 75 Assert.AreEqual(3, MyCreator.Create<IMyTest>().Test11(ref lst)); 76 Assert.AreEqual(1, lst.Count); 77 Assert.AreEqual(3, lst[0]); 78 } 79 80 [TestMethod] 81 public void TestMethod12() 82 { 83 Assert.AreEqual(1, MyCreator.Create<IMyTest>().Test12(TestEnumModel.B)); 84 } 85 86 [TestMethod] 87 public void TestMethod13() 88 { 89 Assert.AreEqual(TestEnumModel.B, MyCreator.Create<IMyTest>().Test13(TestEnumModel.B)); 90 } 91 92 [TestMethod] 93 public void TestMethod14() 94 { 95 Assert.AreEqual(TestEnumModel.B, MyCreator.Create<IMyTest>().Test14(TestEnumModel.B)); 96 } 97 98 [TestMethod] 99 public void TestMethod15() 100 { 101 Assert.AreEqual("B123", MyCreator.Create<IMyTest>().Test14(TestEnumModel.B, 123)); 102 } 103 104 [TestMethod] 105 public void TestMethod16() 106 { 107 Assert.AreEqual("Baaaaa", MyCreator.Create<IMyTest>().Test14(TestEnumModel.B, "aaaaa")); 108 } 109 110 [TestMethod] 111 public void TestMethod17() 112 { 113 TestImpModel m = new TestImpModel() { A = 1, B = "3" }; 114 Assert.AreEqual("B2", MyCreator.Create<IMyTest>().Test14(TestEnumModel.B, m)); 115 Assert.AreEqual(1, m.A); 116 Assert.AreEqual("3", m.B); 117 } 118 119 [TestMethod] 120 public void TestMethod18() 121 { 122 TestImpModel m = new TestImpModel() { A = 1, B = "3" }; 123 var n = MyCreator.Create<IMyTest2<TestImpModel>>().Test(m); 124 Assert.AreEqual(1, n.A); 125 Assert.AreEqual("3", n.B); 126 }
18个简单的测试用例
项目源代码:DynamicWCF.rar