添加wcf服務引用時,vs.net本來就會幫我們在app.config/web.config里生成各種配置,這沒啥好研究的,但本文談到的配置并不是這個。先看下面的圖:
通常,如果采用.NET的WCF技術來架構SOA風格的應用,我們會把項目做一些基本的分層,如上圖:
01. contract層:通常定義服務的接口(即服務契約ServiceContract,指明該服務提供了哪些方法可供外部調用)、以及接口方法中傳輸的Model定義(即:數據契約DataContract,指明方法中的對象參數的Class定義)
02. implementation層:即服務接口的實現
03. host層:wcf最終需要一個宿主環境,如果是web應用,最簡單的辦法莫過于直接寄宿在IIS上
04. client層:即服務的消費方,如果是b/s應用,通常就是一個web application
實際部署時,一般將wcf服務層和client層分開部署,如下圖:
如果并發數隨著業務的增長而增長,不管是client層的website,還是服務層的service,加上其它技術,比如集群或負載均衡之類,可以很方便進行擴充。服務的實現邏輯也可以方便的單獨的修改替換(前提是服務契約相對穩定)
但如果應用的規模較小,出于成本考慮,完全有可能Service層和Website Client部署在一臺機器上,雖然1個IIS上架2個站點完全沒有問題,但是總歸有點不爽,既然都在一臺機器上了,為啥還要自己調用自己,增加無謂的開銷呢?
最好是在不修改原來代碼的前提下,通過簡單的配置文件修改,就能讓原來遠程調用WCF的方式,改成直接調用本地DLL程序集,反過來也一樣,這樣就比較靈活了。事實上,我們公司很多項目就是這樣處理的,規模小的應用,直接全都部署在一臺機器上,等應用規模上去了,再分開部署,代碼完全不用動,只要修改相關配置即可。
原理其實非常簡單,反射即可,先在Client層的web.config或app.config中,增加類似以下節點:
1 <appSettings> 2 <!--調用方式:Remote遠程調用,Local本地調用(注:本地調用時,bin目錄下必須有[服務實現類]的dll)--> 3 <add key="CallType" value="Remote"/> 4 <!--本地調用時,程序集的名稱--> 5 <add key="AssemblyName" value="sjtu.wcf.demo.implementation"/> 6 <!--本地調用時,[服務實現類]的名稱--> 7 <add key="ServiceTypeName" value="sjtu.wcf.demo.implementation.DemoService"/> 8 </appSettings>
CallType就決定了調用方式:“遠程調用”或“本地DLL調用”。然后在本地寫一個調用的Client類:(注:wcf的調用方式,參考了dudu的文章“享受無止境 - 改進版WCF Client”)

1 using System; 2 using System.Linq.Expressions; 3 using System.Reflection; 4 using System.ServiceModel; 5 using sjtu.wcf.demo.client.configs; 6 7 namespace sjtu.wcf.demo.client 8 { 9 /// <summary> 10 /// Wcf客戶端 11 /// </summary> 12 /// <typeparam name="T">ServiceContract接口</typeparam> 13 public class WcfClient<T> where T : class 14 { 15 16 private readonly string assemblyName; 17 private readonly string implTypeName; 18 private readonly string callType; 19 20 public WcfClient() 21 { 22 callType = ConfigHelper.CallType.ToLower(); 23 if (callType == CallType.Local.ToString().ToLower()) 24 { 25 assemblyName = ConfigHelper.AssemblyName; 26 implTypeName = ConfigHelper.ServiceTypeName; 27 } 28 } 29 30 /// <summary> 31 /// 對外提供的Call方法 32 /// </summary> 33 /// <typeparam name="R"></typeparam> 34 /// <param name="expression"></param> 35 /// <returns></returns> 36 public R Call<R>(Expression<Func<T, R>> expression) 37 { 38 if (callType == CallType.Local.ToString().ToLower()) 39 { 40 return InvokeLocalMethod<R>(expression); 41 } 42 return InvokeRemoteMethod<R>(expression); 43 } 44 45 /// <summary> 46 /// 調用本地程序集方法 47 /// </summary> 48 /// <typeparam name="R"></typeparam> 49 /// <param name="operation"></param> 50 /// <returns></returns> 51 private R InvokeLocalMethod<R>(Expression<Func<T, R>> operation) 52 { 53 Assembly asm = Assembly.Load(new AssemblyName(assemblyName)); 54 T t = (T)asm.CreateInstance(implTypeName); 55 R result = operation.Compile().Invoke(t); 56 return result; 57 } 58 59 /// <summary> 60 /// 調用遠程wcf方法 61 /// </summary> 62 /// <typeparam name="R"></typeparam> 63 /// <param name="operation"></param> 64 /// <returns></returns> 65 private R InvokeRemoteMethod<R>(Expression<Func<T, R>> operation) 66 { 67 ChannelFactory<T> channelFactory = new ChannelFactory<T>("*"); 68 69 T channel = channelFactory.CreateChannel(); 70 var client = (IClientChannel)channel; 71 client.Open(); 72 R result = operation.Compile().Invoke(channel); 73 try 74 { 75 if (client.State != CommunicationState.Faulted) 76 { 77 client.Close(); 78 } 79 } 80 catch 81 { 82 client.Abort(); 83 } 84 return result; 85 } 86 } 87 }
這樣調用時,只需要一行代碼即可:
1 var students = new WcfClient<IStudent>().Call(c => c.GetStudents("jerry"));
文章列表