2010-10-07 54 views
1

我想將SOA模式合併到我的3層結構中。我在BLL和UI之間創建了一個服務層(WCF主機)。 我的結構設置是現在看起來是這樣SOA問題:公開實體

UI <> WCF <> BLL <> DAL

<---[Entities] ---> 

的問題是,我有我在單獨的DLL實體(和它在除用戶界面外的所有圖層中都可見) 現在,我需要公開它,以便我的服務的使用者可以使用它。在這種情況下,UI。我怎麼可能做到這一點?

Entities.DLL

namespace Entities 
    { 
     public class Account 
     { 
      public string AcctID { get; set; } 
      public string AcctName { get; set; } 
     } 
    } 

現在而言,我打算使用它在WCF

服務接口層

public class AccountService : IAccountService 
    { 

     public Account GetAccount(string AcctID) 
     { 
      //fetch from DAL through BLL 
      } 
    } 

它是確定,只是屬性我的實體? (注意,我也在使用DAL和BLL中的實體)

using System.Runtime.Serialization; 
    namespace Entities 
    { 
     [DataContract] 
     public class Account 
     { 
      [DataMember] 
      public string AcctID { get; set; } 

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

任何建議傢伙?

回答

2

下面是爲我們工作的系統:

您應該通常使用反映你期待需要在客戶端的數據的數據傳輸對象。業務層應該定義這些DTO以及它們的存儲庫接口。數據層應該實現存儲庫接口,將數據層實體轉換爲DTO。 WCF層應該簡單地成爲各種存儲庫接口方法的向外包裝器。

這樣一來,它看起來更像是這樣的:

 
UI ---\ 
    |  BLL -- DAL 
WCF---/ 
    [  DTO ] 
    [Repositories] 
       [Entities] 

在我的腦海裏,我看到了WCF層作爲UI層的一部分,所以我覺得他倆都知道沒事在業務層中定義的對象。但是,你可以把它一步,使WCF層負責業務轉換的對象爲DTO的:

 
UI -- WCF -- BLL -- DAL 
[ DTOs ] 
     [  Repositories  ] 
     [ Business Objects ] 
           [Entities] 

通過這種方式,每一層只知道最多每側單層的。 DTO可以註釋爲序列化或其他,因爲它們實際上只是爲此目的而設計的。只有數據訪問層瞭解您的數據實體。

在響應到您的評論:

如果你的實體在你的數據訪問層定義,那麼他們真的是沒有DTO的。他們正在爲您的數據層建模,該數據層不一定會直接轉換爲您在UI中需要的對象。

我建議爲存儲庫定義接口的原因是,您可以使用依賴注入來放鬆WCF層和業務層之間的耦合。這也將使您的WCF圖層單元可測試,因爲您可以創建模擬特定情況的假或模擬存儲庫實現。

在我推薦的第一款車型,您的WCF方法看起來幾乎完全一樣你的資料庫的方法,所以一個典型的WCF將基本上只是「包裝」一庫方法:

public IEnumerable<Task> GetActiveTasks() { 
    return _taskRepository.GetActiveTasksForUser(_sessionManager.CurrentUser); 
} 
+0

感謝瘦長爲詳細的說明。我還沒有嘗試實體到DTO方法的映射,所以我有很多的研究要做,我認爲這是要走的路。但我只關注DTO和實體,ENL對BLL和DAL可見,而DTO將用於從WCF到UI的數據傳輸。 WCF將處理映射.. – CSharpNoob 2010-10-07 15:58:27

+0

這是一個很好的開始。我知道,看起來幾乎完全一樣的DTO和實體聽起來有點多餘,但如果您的項目變得非常龐大,它將帶來收益。 – StriplingWarrior 2010-10-07 16:05:15

+0

你可以在我的答案中加入存儲庫嗎?我會在哪裏放?謝謝先生.. – CSharpNoob 2010-10-07 16:42:55

1

我可以告訴你我是怎麼做到這一點。

我有單獨的DTO和實體 - 它並不總是1:1的關係。我真的不喜歡在我的實體中具有所有的屬性。 (另外,如果你想在兩者之間輕鬆轉換打破封裝的所有屬性都突然讀寫

- 。有圖書館,以方便(IER),如AutoMapper

如果使用實體作爲DTO,您經常會發送太多的數據 - 例如,一個訂單中有一個賬戶擁有多個訂單類型的OpenOrders。每次獲取一筆訂單時,您也將獲得該賬戶的所有未結訂單。將在UI上使用與我在服務層中使用的相同的Business-dll - 因此我可以在將它發送到服務器之前在客戶端進行驗證。當然,此部分是可選的 - 您也可以複製lo gic(但我討厭重複:-))。

希望這會讓你有點方向。

+0

對我來說聽起來多餘(映射ENtity-DTO)並且還有額外的編碼。有沒有將DTO和ENtity合爲一體的方法? – CSharpNoob 2010-10-07 15:46:36

+0

這不是多餘的 - 它是關注點的分離 - 將所有責任放在一個大塊頭上是創造維護噩夢的一個可靠方法。 – Goblin 2010-10-07 17:41:03

1

我想我使用AutoMapper得到了IT。

我最後通過WCF ..the簡單的方式暴露實體作爲DTO ..

實體

namespace Entities 
{ 
    public class Account 
    { 
    public string AccountNo { get; set; } 
    public string AccountName { get; set; } 
    } 
} 

BLL

namespace BLL 
{ 
    // This defines the repository interface. 
    public interface IAccountRepository 
    { 
    Account GetAccount(int accountId); 
    } 

    public class AccountRepository 
    { 
    public Account GetAccount(int accountId) { 
     // get the Account object from the data layer. 
    } 
    } 

    // Using an interface makes it easy to swap various implementations. 
    // The implementation above would be the one you'd normally use, but you could 
    // create others like this to help with unit testing and such. 
    public class FakeAccountRepository : IAccountRepository 
    { 
    public Account GetAccount(int accountId) 
    { 
     return new Account { AccountName = "JuanDeLaCruz", AccountNo = "123456" }; 
    } 
    } 
} 

WCF

[ServiceContract] 
public interface IService 
{ 
    [OperationContract] 
    AccountDTO GetAccount(int accountId); 
} 


[DataContract] 
public class AccountDTO 
{ 
    [DataMember] 
    public string AccountNo { get; set; } 
    [DataMember] 
    public string AccountName { get; set; } 
} 


public class Service : IService 
{ 
    // Define a Factory in your .svc file to inject a repository implementation. 
    // It's best to use an IoC container like Ninject for this sort of thing. 
    public Service(// no pun intended 
     IAccountRepository accountRepository) 
    { 
    _accountRepository = accountRepository 
    } 

    public AccountDTO GetAccount(int accountId) 
    { 
    Mapper.CreateMap<Account, AccountDTO>(); 
    var account = _accountRepository.GetAccount(accountId); 
    var accountDto = Mapper.Map<Account, AccountDTO>(account); 
    return account; 
    } 
} 

WCF .aspx的消費者

protected void Page_Load(object sender, EventArgs e) 
{ 
    ServiceClient accountService= new ServiceClient(); 
    AccountDTO account = accountService.GetAccount(); 
    Response.Write(account.AccountName); 
} 

的任何建議/更正傢伙請評論.. ^^

多虧了Stiffling爵士和地精

+0

按照要求,我修改了這個以包含存儲庫接口。 – StriplingWarrior 2010-10-07 17:32:25

+0

似乎聽起來對我來說:-)只是一個細節 - 你可能只想運行Mapper.CreateMap ();一旦。每次創建它要麼花費太高 - 要麼第二次調用它時會拋出。 – Goblin 2010-10-07 19:40:18

+0

你能否回答http://stackoverflow.com/questions/9480869/alternatives-for-dto-domain-translation-layer-in-soa-wcf? – Lijo 2012-02-29 05:43:58

1

的DTO是非常好的方法,在某些情況下,它們是絕對必要的。其中一些情景如下:

  • 大項目 - 與其他場景一起使用。分離問題很重要。
  • 實體是域對象=包含業務邏輯
  • 實體以某種方式。NET的具體和服務必須從其他平臺使用
  • 服務層爲每個操作公開專門的數據類型,而不是CRUDy接口。例如,選擇操作可以使用創建日期,上次修改日期等數據返回對象。但更新操作無需從客戶端傳輸此數據,因此它使用DTO而不使用這些字段。你通常走得更遠,你沒有純粹的選擇和更新,但一些真正的業務功能。

另一方面,您的架構應該由您的要求以及應用程序的預期複雜性和大小驅動。 DTO涉及大量額外工作=額外成本。對於較小的簡單項目,您的服務將僅由您使用.NET編寫的UI客戶端來使用,在單獨的程序集中定義您的實體沒有任何問題,用序列化和數據註釋的屬性標記這些實體(可用於驗證客戶端和服務器端)或者與其他驗證屬性(例如來自企業庫的驗證應用程序塊)以及在包括UI客戶端在內的所有應用程序層之間共享此程序集。首先簡單。

0

如果你想在客戶端上暴露服務合同simpliest方式是這樣的

[Serializable] 
public class Accountd 
{ 
    public string AccountNo { get; set; } 
    public string AccountName { get; set; } 
} 

,如果你需要在其成員應該越過邊界更多的控制權,然後使用這個

[DataContract] 
public class Account 
{ 
    [DataMember] 
    public string AccountNo { get; set; } 
    public string AccountName { get; set; } **// client won't see this data.** 
}