自從在《Winform開發框架之插件化應用框架實現》一文中,介紹並總結了Winform開發框架插件化應用框架的實現后,贏得了很多同行和客戶的支持,於是把我的WCF開發框架、混合式開發框架都進行了升級,把它們都提升到插件化應用的高度上。本文主要介紹WCF開發框架,如何實現插件化的應用。從我隨筆《基於我的Winform開發框架擴展而成的WCF開發框架》介紹可以看到,一般的WCF應用,是在客戶端添加服務應用的方式,然后使用自動生成的WCF服務客戶端代理來訪問相應的服務的,這種方式比較方便快捷,但是也增加了客戶端界面和WCF服務的耦合性,架構布局如下所示。
為了更好有效利用松耦合的特點,以及插件化應用的特點,我對整個WCF開發框架進行了一個大的調整,以便更好整合及利用好的特點。如下圖所示,插件化應用框架的啟動模塊,它除了依賴基礎服務模塊中(包括權限管理模塊和字典模塊),其他的模塊如WCF服務模塊、主業務插件模塊A(主業務模塊B)等都是通過配置方式實現對接的,他們之間沒有明顯的耦合關系。
整個項目工程的布局如下所示,其中BaseUIDx為基礎界面類,方便各個插件模塊重用而分離出來的。其他模塊的功能如上圖所示。
主業務插件模塊是指各種各種的插件化業務模塊,他們本身包含有界面部分、WCF服務調用、以及業務邏輯等內容。
為了更好分離WCF服務的部署和WCF服務邏輯(這也是最佳實踐),實現更好的代碼控制和重用,WCF項目的架構關系設置成如下所示。
其中WHC.WareHouseMis.WCFLibrary是整個WCF服務的業務邏輯模塊,它囊括了統一調用接口Facade層、WCF業務實現WCFLibrary層、BLL業務層、數據接口IDAL層、數據訪問實現DAL層、實體類Entity層。
當然,以上的關系不需要手工來做這些繁瑣的代碼對應關系,只需要設計好表后,使用代碼生成工具Database2Sharp一鍵生成就可以了,其中很多項目關系已經生成好了,增量開發的時候,重新引用下文件關系即可。
而且整個WCF不在使用在界面層直接引用WCF服務的方式,而采用了自定義的客戶端信道(允許從自定義的配置文件中加載)方式實現對應WCF服務客戶端代理類的創建。
以WCF的調用類ItemDetailCaller為例,使用代碼生成工具生成的代碼,它已經繼承了某個接口IItemDetailService服務基類了,並給它指定了具體的WCF服務節點即可,如下代碼所示。
public class ItemDetailCaller : BaseService<ItemDetailInfo>, IItemDetailService { public ItemDetailCaller() : base() { this.endpointConfigurationName = EndPointConfig.ItemDetailService; }
實現接口IItemDetailService的函數很有規律,使用下面類似的代碼即可。
public List<ItemDetailInfo> FindByBigType(string bigType) { List<ItemDetailInfo> result = new List<ItemDetailInfo>(); IItemDetailService service = CreateSubClient(); ICommunicationObject comm = service as ICommunicationObject; comm.Using(client => { result = service.FindByBigType(bigType); }); return result; }
這樣進行了包裝后,我們使用WCF服務就好像之前的Winform開發框架使用BLLFactory的方式一樣了,WCF服務調用示例代碼如下所示。
bool succeed = CallerFactory<IItemDetailService>.Instance.Update(info, info.ID.ToString()); if (succeed) { try { StockInfo stockInfo = CallerFactory<IStockService>.Instance.FindByItemNo2(this.txtItemNo.Text, this.txtBelongWareHouse.Tag.ToString()); if (stockInfo != null) { stockInfo.WareHouse = txtBelongWareHouse.Text; CallerFactory<IStockService>.Instance.Update(stockInfo, stockInfo.ID.ToString()); } //不管是更新還是新增,如果對應的備件編碼在庫房沒有初始化,初始化之 bool isInit = CallerFactory<IStockService>.Instance.CheckIsInitedWareHouse(this.txtBelongWareHouse.Text, this.txtItemNo.Text); if (!isInit) { CallerFactory<IStockService>.Instance.InitStockQuantity(info, 0, this.txtBelongWareHouse.Text); } } catch (Exception ex) { MessageDxUtil.ShowTips(string.Format("初始化庫存為0失敗:", ex.Message)); } return true; }