1. 程式人生 > >C#進階系列——DDD領域驅動設計初探(四):WCF搭建

C#進階系列——DDD領域驅動設計初探(四):WCF搭建

前言:前面三篇分享了下DDD裡面的兩個主要特性:聚合和倉儲。領域層的搭建基本完成,當然還涉及到領域事件和領域服務的部分,後面再專案搭建的過程中慢慢引入,博主的思路是先將整個架構走通,然後一步一步來新增相關元素,使架構慢慢變得豐滿。這篇打算分享下應用層的搭建。根據DDD的設計原則,應用層不包含任何領域邏輯,它主要的作用是協調任務,或者叫排程任務,維護應用程式狀態。根據博主的理解,應用層是用來隔離領域層的,假設沒有應用層,那麼我們的介面層可以直接呼叫領域層的邏輯,也就是說可以直接訪問領域的model,這樣的壞處顯而易見:一是領域model不是純粹的資料model,它含有領域的行為,直接將其傳到前臺會造成呼叫的混亂;二是倉儲是和資料持久化打交道了,介面直接呼叫倉儲,也就是介面直接和資料打交道,也不符合一般分層的原則。所以我們引入應用層,本文應用層是一個以控制檯專案為宿主的WCF服務。我們來看程式碼設計。

DDD領域驅動設計初探系列文章:

一、WCF簡介

WCF(Windows Communication Foundation)是由微軟發展的一組資料通訊的應用程式開發介面,可以翻譯為Windows通訊介面,它是.NET框架的一部分。由 .NET Framework 3.0 開始引入。WCF的最終目標是通過程序或不同的系統、通過本地網路或是通過Internet收發客戶和服務之間的訊息。關於WCF的理論知識,需要我們瞭解的是經典的ABC。

  • Address: 每一個WCF的Service都有一個唯一的地址。這個地址給出了Service的地址和傳輸協議(Transport Protocol)。
  • Binding:綁定製定了服務通過什麼形式訪問。只要類比傳輸協議, encoding (text, binary, etc) 以及 WS-* 協議,像transactional支援以及可信任的訊息佇列。
  • Contract:Contract描述了Service能提供的各種服務。Contract有四種,包括Service Contract, Data Contract, Fault Contract和Message Contract。

關於WCF的理論在此就不再展開,下面結合我們的專案程式碼我們從零開始一步一步來搭建一個自己的WCF服務吧。

二、WCF程式碼示例

1、程式碼結構圖 

專案按照模組為單位劃分服務,比如許可權模組,我們就有一個許可權的介面契約IPowerManageWCFService。IService資料夾裡面放了3個介面,分別對應系統3個模組的介面契約,Service資料夾裡面分別對應了3個介面的實現。ServiceAttribute.cs裡面定義了兩個特性,表示介面是WCF的服務。我們來看看具體的程式碼。

2、程式碼示例

2.1 ServiceAttribute.cs檔案定義契約介面和實現的特性類:

namespace ESTM.WCF.Service
{
    //標記此特性的為WCF服務介面
    public class ServiceInterfaceAttribute : Attribute
    {
    }

    //標記此特性的為WCF服務介面實現類
    public class ServiceClassAttribute : Attribute
    {
    }
}

2.2 介面契約程式碼:

    /// <summary>
    /// 工廠佈局模組介面契約
    /// </summary>
    [ServiceInterface]
    [ServiceContract]
    public interface IFactoryLayoutWCFService
    {
        [OperationContract]
        List<DTO_TM_PLANT> GetAllPlant();
    }
    /// <summary>
    /// 許可權管理模組介面契約
    /// </summary>
    [ServiceContract]
    [ServiceInterface]
    public interface IPowerManageWCFService
    {
        [OperationContract]
        IList<DTO_TB_DEPARTMENT> GetAllDepartment();
    }
    /// <summary>
    /// 產品管理模組介面契約
    /// </summary>
    [ServiceContract]
    [ServiceInterface]
    public interface IProductWCFService
    {
        [OperationContract]
        IList<DTO_TP_PRODUCT> GetAllProduct();
    }

介面契約[ServiceContract]表示該介面遵守介面契約協定,[OperationContract]操作契約,這兩個特性都是WCF內建的東西。[ServiceInterface]的用處我們待會說。

2.3 介面實現程式碼

    [ServiceClass]
    public class FactoryLayoutWCFService : IFactoryLayoutWCFService
    {
        public List<DTO_TM_PLANT> GetAllPlant()
        {
            throw new NotImplementedException();
        }
    }
    [ServiceClass]
    public class ProductWCFService : IProductWCFService
    {
        public IList<DTO_TP_PRODUCT> GetAllProduct()
        {
            throw new NotImplementedException();
        }
    }
    [ServiceClass]
    public class PowerManageWCFService : IPowerManageWCFService
    {
        public IList<DTO_TB_DEPARTMENT> GetAllDepartment()
        {
            throw new NotImplementedException();
        }
    }

[ServiceClass]特性和介面上面的[ServiceInterface]特性對應,用於標記契約和實現。

2.4 Bootstrapper.cs裡面定義了服務的啟動方法

   public class Bootstrapper
    {
        private string strBaseServiceUrl = ConfigurationManager.AppSettings["ServiceUrl"].ToString();

        //啟動所有的服務
        public void StartServices()
        {
            //1.讀取此程式集裡面的有服務契約的介面和實現類
            var assembly = Assembly.Load(typeof(Bootstrapper).Namespace);
            var lstType = assembly.GetTypes();
            var lstTypeInterface = new List<Type>();
            var lstTypeClass = new List<Type>();
            foreach (var oType in lstType)
            {
                //2.通過介面上的特性取到需要的介面和實現類
                var lstCustomAttr = oType.CustomAttributes;
                if (lstCustomAttr.Count() <= 0)
                {
                    continue;
                }
                var oInterfaceServiceAttribute = lstCustomAttr.FirstOrDefault(x => x.AttributeType.Equals(typeof(ServiceInterfaceAttribute)));
                if (oInterfaceServiceAttribute != null)
                {
                    lstTypeInterface.Add(oType);
                    continue;
                }
                var oClassServiceAttribute = lstCustomAttr.FirstOrDefault(x => x.AttributeType.Equals(typeof(ServiceClassAttribute)));
                if (oClassServiceAttribute != null)
                {
                    lstTypeClass.Add(oType);
                }                
            }

            //3.啟動所有服務
            foreach (var oInterfaceType in lstTypeInterface)
            {
         //通過反射找到介面的實現類,找到配對然後啟動服務
var lstTypeClassTmp = lstTypeClass.Where(x => x.GetInterface(oInterfaceType.Name) != null).ToList(); if (lstTypeClassTmp.Count <= 0) { continue; } if(lstTypeClassTmp[0].GetInterface(oInterfaceType.Name).Equals(oInterfaceType)) { var oTask = Task.Factory.StartNew(() => { OpenService(strBaseServiceUrl + "/" + oInterfaceType.Name, oInterfaceType, lstTypeClassTmp[0]); }); } } } //通過服務介面型別和實現型別啟動WCF服務 private void OpenService(string strServiceUrl, Type typeInterface, Type typeclass) { Uri httpAddress = new Uri(strServiceUrl); using (ServiceHost host = new ServiceHost(typeclass)) { ///////////////////////////////////////新增服務節點/////////////////////////////////////////////////// host.AddServiceEndpoint(typeInterface, new WSHttpBinding(), httpAddress); if (host.Description.Behaviors.Find<ServiceMetadataBehavior>() == null) { ServiceMetadataBehavior behavior = new ServiceMetadataBehavior(); behavior.HttpGetEnabled = true; behavior.HttpGetUrl = httpAddress; host.Description.Behaviors.Add(behavior); } host.Opened += delegate { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("服務啟動成功。服務地址:" + strServiceUrl); }; host.Open(); while (true) { Console.ReadLine(); } } } }

對應的App.Config裡面對應的服務的URL

  <appSettings>
    <add key="ServiceUrl" value="http://127.0.0.1:1234/MyWCF.Server"/>
  </appSettings>

StartServices()方法通過反射和兩個特性[ServiceClass]與[ServiceInterface],依次啟動三個服務。

然後再Program裡面呼叫

     static void Main(string[] args)
        {
            var oBootstrapper = new Bootstrapper();
            oBootstrapper.StartServices();
            Console.ReadLine();
        }

得到結果:

我們隨便選擇一個服務,通過瀏覽器訪問,測試服務是否啟動成功。

至此,WCF服務基本完成。

三、DTO說明

DTO,全稱Data Transfer Object,資料傳輸物件。DTO是一個貧血模型,也就是它裡面基本沒有方法,只有一堆屬性,並且所有屬性都具有public的getter和setter訪問器。為什麼需要一個DTO物件?這個問題在C#進階系列——MEF實現設計上的“鬆耦合”(終結篇:面向介面程式設計)這篇裡面介紹過,它的作用其實很單一,就是用於資料傳遞和資料繫結。至於DTO如何設計,博主的專案裡,DTO是按照聚合來劃分的,也就是一個聚合對應一個DTO,DTO裡面屬性的定義可以根據專案需求來定。我們來看看程式碼:

    /// <summary>
    /// 所有DTO model的父類,用作泛型約束
    /// </summary>
    [DataContract]
    public class DTO_BASEMODEL
    {
    }
/// <summary>
    /// TB_DEPARTMENT
    /// </summary>
    [DataContract]
    public class DTO_TB_DEPARTMENT : DTO_BASEMODEL
    {
        [DataMember]
        public string DEPARTMENT_ID { get; set; }

        [DataMember]
        public string DEPARTMENT_NAME { get; set; }

        [DataMember]
        public string PARENT_ID { get; set; }

        [DataMember]
        public string DEPARTMENT_LEVEL { get; set; }

        [DataMember]
        public string STATUS { get; set; }
    }

其他DTO都和這個類似,就不一一列舉了。由於DTO需要由WCF傳遞到Web前臺,所以要求這個物件可以序列化,需要標記[DataContract]和[DataMember]兩個特性,DTO_BASEMODEL作為所有DTO的父類, 用作泛型約束和定義DTO的一些公用特性。到此,WCF的搭建基本完成,下篇我們來介紹下Automapper的使用。累死我了,今天先到這吧,也不早了,博主也要安歇了。晚安!

還是附上 原始碼 吧,要不然園友們要說博主小氣呢~~有興趣可以看看。

相關推薦

C#系列——DDD領域驅動設計初探WCF搭建

前言:前面三篇分享了下DDD裡面的兩個主要特性:聚合和倉儲。領域層的搭建基本完成,當然還涉及到領域事件和領域服務的部分,後面再專案搭建的過程中慢慢引入,博主的思路是先將整個架構走通,然後一步一步來新增相關元素,使架構慢慢變得豐滿。這篇打算分享下應用層的搭建。根據DDD的設計原則,應用層不包含任何領域邏輯,它主

C#系列——DDD領域驅動設計初探領域服務

前言:之前一直在搭建專案架構的程式碼,有點偏離我們的主題(DDD)了,這篇我們繼續來聊聊DDD裡面另一個比較重要的知識點:領域服務。關於領域服務的使用,書中也介紹得比較晦澀,在此就根據博主自己的理解談談這個知識點的使用。 DDD領域驅動設計初探系列文章: 一、領域服務的引入 在《領域驅動設計:軟體核

C#系列——DDD領域驅動設計初探AutoMapper使用

前言:前篇搭建了下WCF的程式碼,就提到了DTO的概念,對於為什麼要有這麼一個DTO的物件,上章可能對於這點不太詳盡,在此不厭其煩再來提提它的作用: 從安全上面考慮,領域Model都帶有領域業務,讓Client端引用Domain Model就意味著Client端可以繞過應用層直接完成業務邏輯的呼叫,這樣

C#系列——DDD領域驅動設計初探倉儲Repository

前言:上篇介紹了下倉儲的程式碼架構示例以及簡單分析了倉儲了使用優勢。本章還是繼續來完善下倉儲的設計。上章說了,倉儲的最主要作用的分離領域層和具體的技術架構,使得領域層更加專注領域邏輯。那麼涉及到具體的實現的時候我們應該怎麼做呢,本章就來說說倉儲裡面具體細節方便的知識。 DDD領域驅動設計初探系列文章:

C#系列——DDD領域驅動設計初探倉儲Repository

前言:上篇介紹了DDD設計Demo裡面的聚合劃分以及實體和聚合根的設計,這章繼續來說說DDD裡面最具爭議的話題之一的倉儲Repository,為什麼Repository會有這麼大的爭議,博主認為主要原因無非以下兩點:一是Repository的真實意圖沒有理解清楚,導致設計的紊亂,隨著專案的橫向和縱向擴充套件,

C#系列——DDD領域驅動設計初探Web層的搭建

前言:好久沒更新部落格了,每天被該死的業務纏身,今天正好一個模組完成了,繼續來完善我們的程式碼。之前的六篇完成了領域層、應用層、以及基礎結構層的部分程式碼,這篇打算搭建下UI層的程式碼。 DDD領域驅動設計初探系列文章: 一、UI層介紹 在DDD裡面,UI層的設計也分為BS和CS,本篇還是以Web為

C#系列——DDD領域驅動設計初探聚合

前言:又有差不多半個月沒寫點什麼了,感覺這樣很對不起自己似的。今天看到一篇博文裡面寫道:越是忙人越有時間寫部落格。呵呵,似乎有點道理,博主為了證明自己也是忙人,這不就來學習下DDD這麼一個聽上去高大上的東西。前面介紹了下MEF和AOP的相關知識,後面打算分享Automapper、倉儲模式、WCF等東西的,可是

C#系列——WebApi 異常處理解決方案

機制 輸出 ges 如果 但是 rom lba slist 解決 出處:http://www.cnblogs.com/landeanfen/p/5363846.html 閱讀目錄 一、使用異常篩選器捕獲所有異常 二、HttpResponseException自

大型Java專題(二) 軟體架構設計原則

## 前言 ​ 今天開始我們專題的第一課了,也是我開始進階學習的第一天,我們先從經典設計思想開始,看看大牛市如何寫程式碼的,提升技術審美、提高核心競爭力。本章節參考資料書籍《Spring 5核心原理》中的第一篇 Spring 內功心法(沒有電子檔,都是我取其精華並結合自己的理解,一個字一個字手敲出來的)。

設計模式原型模式

原型模式 UML類圖:   說明:   在Java中不需要ProtoType介面,Java自帶克隆介面:Cloneable,只需ConcreteProtoType直接實現Cloneable介面,之後重寫 clone()方法即可。 優點:   ①隱藏了新物件建立的細節,大大提高了效能,

Java 設計模式單例模式

參考連結:單例模式-Singleton Pattern 對於一個軟體系統的某些類而言,我們無須建立多個例項。舉個大家都熟知的例子——Windows工作管理員,如圖所示: 通常情況下,無論我們啟動任務管理多少次,Windows系統始終只能彈出一個工作管理員視窗。為什麼要這樣設計呢?我

設計模式代理模式

前言 國內程式設計師好像普遍對百度都沒好感,而且百度近些年產生了不少負面的新聞,像16年的魏則西事件,近期的導演吳京黑白照事件,以及最近作家六六斥百度李彥巨集:“你是做搜尋引擎還是騙子首領”,還有一件就是與程式設計師有關的:搜尋Julia語言,在百度和Google得出首條搜尋結果的差異性而被吐槽。Google

設計模式】HeadFirst設計模式工廠模式

     設計模式要求我們不應該針對實現程式設計,是為了降低耦合度,提高可維護性。當程式中出現“new”的時候,就證明程式在例項化一個具體類,所以用的是實現,而不是介面。如果程式碼綁著具體的類會導致程

人工智慧中小樣本問題相關的系列模型演變及學習筆記知識蒸餾、增量學習

【說在前面】本人部落格新手一枚,象牙塔的老白,職業場的小白。以下內容僅為個人見解,歡迎批評指正,不喜勿噴![握手][握手] 【再囉嗦一下】本文銜接上兩個隨筆:人工智慧中小樣本問題相關的系列模型演變及學習筆記(一):元學習、小樣本學習 【再囉嗦一下】本文銜接上兩個隨筆:人工智慧中小樣本問題相關的系列模型演變及學

EF Code first 和 DDD (領域驅動設計研究)系列

發的 tex bsp cti 設計 ron 映射 developer devel 在上個公司工作時,開發公司產品的過程中,接觸到了EF Code first. 當時,整個產品的架構都是Lead developer設計建立的,自己也不是特別理解,就趕鴨子上架跟著一起開發了。

【9】C++系列泛型設計以及STL標準模板庫

1、泛型程式設計基本概念 泛型程式設計:編寫不依賴與具體資料型別的程式,將演算法從特定的資料結構中抽象出來,成為通用的。C++的模板為泛型程式設計定義了關鍵的基礎。 兩個術語:概念,模型 概念:用來界定具備一定功能的資料型別,例如:將“可以比較大小的所有資料型別(有比較

C#系列——MEF實現設計上的“鬆耦合”

前言:前篇 C#進階系列——MEF實現設計上的“鬆耦合”(一) 介紹了下MEF的基礎用法,讓我們對MEF有了一個抽象的認識。當然MEF的用法可能不限於此,比如MEF的目錄服務、目錄篩選、重組部件等高階應用在這裡就不做過多講解,因為博主覺得這些用法只有在某些特定的環境下面才會用到,著實不太普遍,感覺沒有鑽下去的

C#系列——MEF實現設計上的“鬆耦合”

前言:最近去了趟外地出差,介紹推廣小組開發的框架類產品。推廣物件是本部門在專案上面的同事——1到2年工作經驗的初級程式設計師。在給他們介紹框架時發現很多框架設計層面的知識他們都沒有接觸過,甚至沒聽說過,這下囧了~~於是乎在想該如何跟他們解釋MEF、AOP、倉儲模式等方面的東東。本來 C#基礎系列 應該還有兩篇

C#系列——MEF實現設計上的“鬆耦合”建構函式注入

前言:今天十一長假的第一天,本因出去走走,奈何博主最大的樂趣是假期坐在電腦前看各處堵車,順便寫寫部落格,有點收穫也是好的。關於MEF的知識,之前已經分享過三篇,為什麼有今天這篇?是因為昨天分享領域服務的時候,用到MEF的注入有參建構函式的方法,博主好奇心重,打算稍微深挖一下,這篇來對此知識點做個總結。 還是

C#系列——MEF實現設計上的“鬆耦合”終結篇面向介面程式設計

序:忙碌多事的八月帶著些許的倦意早已步入尾聲,金秋九月承載著抗戰勝利70週年的喜慶撲面而來。沒來得及任何準備,似乎也不需要任何準備,因為生活不需要太多將來時。每天忙著上班、加班、白加班,忘了去憤,忘了去算計所謂的價值。天津爆炸事故時刻警示著我們生命的無常,逝者安息,活著的人生活還得繼續,珍惜生命,遠離傷害。武