2015-12-04 15 views
2

我正在開發一個WCF項目,我想實現一個RICH域模型,客戶端對象充當服務器端功能的遠程外觀但與客戶端/服務器之間的某些共享方面,如驗證。比方說,我有兩個方法的Order類:Save()和Submit()。在服務器上,Save()方法將寫入數據庫,並且Submit()會將訂單發送給供應商的系統。在客戶端對象充當遠程外牆的WCF中實現豐富的域模型

我想在客戶端上鏡像豐富的域模型,但不想在Save()方法中寫入數據庫,我想運行驗證碼,然後在WCF服務接口上調用SaveOrder(this)。這將遵循Fowler的服務層+域模型模式。理想情況下,我想編寫AbstractOrder基類,實現所有共享功能並指定抽象函數,然後實現充當服務服務器的ClientOrder,ServerOrder和WCF接口IOrderService(具有Save(AbstractOrder)和Submit(AbstractOrder)) -側。 ClientOrder的Save()/ Submit()會調用IOrderService上的保存/提交方法,並在方法調用期間傳遞它自己。

有沒有辦法指示WCF哪些對象實例化和反序列化內容?我特別想在整個應用程序中使用對象的抽象版本,只有在反序列化時才能確定我是否需要客戶端/服務器端版本的對象?我們已經定製了WCF通信渠道:我們使用protobuf和gzip壓縮技術來實現客戶端/服務器和Ninject之間的數據傳輸以實現服務實例化。理想情況下,我想將對象實例化卸載到Ninject。

我特別不希望訂單類是一個WCF服務,因爲我正在處理一個相當胖的客戶端,其中需要大量的邏輯來保持系統在設定的限制內執行,我最好不要想要結束一個貧血的領域模型,其中大多數邏輯被塞進服務中。

在代碼中它會是這樣的:

[ServiceContract] 
public interface IOrderService 
{ 
    [OperationContract] 
    AbstractOrder GetById(int id); 
    [OperationContract] 
    IEnumerable<AbstractOrder> GetBySupplier(int supplierId); 
    [OperationContract] 
    void Save(AbstractOrder order); 
    [OperationContract] 
    void Submit(AbstractOrder order); 
} 

public abstract class AbstractOrder() 
{ 
    public int Id { get; set; } 
    public string Description { get; set; } 
    public List<AbstractOrderline> OrderLines { get; set; } 

    public abstract void Save(); 
    public abstract void Submit(); 
} 

public class ClientOrder : AbstractOrder 
{ 
    public override void Save() 
    { 
     ValidateOrThrow(); 
     _service.Save(this); 
    } 

    public override void Submit() 
    { 
     ValidateOrThrow(); 
     _service.Submit(this); 
    } 
} 

public class ServerOrder : AbstractOrder 
{ 
    public override void Save() 
    { 
     ValidateOrThrow(); 
     _unitOfWork.Save(this); 
    } 

    public override void Submit() 
    { 
     Save(); 

     _supplierOrderService.Submit(this); 
    } 
} 
+1

您有五個段落開頭的一,無論如何,如果你想分享的WCF服務和CLIEN之間DTO類型t,它們在整個調用堆棧中的類型完全相同。您可以隨時在該Order類中引入IOrderService,並通過屬性設置IOrderService。在服務器上實現它的類將寫入數據庫或調用其他服務,您在客戶端上設置的服務將調用WCF服務。 – CodeCaster

+0

問題是,我不想將域模型轉換爲DTO,然後再轉換回客戶端上的域模型。福勒特別寫道,在當地環境中使用DTO可能有害,並且使API更難使用(http://martinfowler.com/bliki/LocalDTO.html)。 這個想法是將ServerOrder傳輸到客戶端,並將其反序列化爲ClientOrder實例。我們使用protobuf進行(反)序列化,所以我目前正在考慮是否有可能影響在反序列化期間創建哪個對象,有可能通過將其重新引導至Ninject。 – Toolmaker

+0

我知道WCF的邊界意味着'偏遠',但因爲我們的客戶/服務器應用程序共享大量代碼,我們認爲它們之間的通信是本地的。 – Toolmaker

回答

1

默認情況下,你不能做到這一點:

// Interface 
AbstractOrder IOrderService.GetById(int); 

// Service 
AbstractOrder OrderService.GetById(int id) 
{ 
    return new ServiceOrder(...); 
} 

// Client 
ClientOrder = (ClientOrder)IOrderService.GetById(42); 

因爲該服務返回的順序並不一個ClientOrder。使用一些反射和一個custom formatter你應該能夠走很長的路。

或者,您可以依靠組合,而不是繼承。引入IRepository<T>(或給它一個名字)在您的共享代碼和上你的模型創建一個屬性:

public interface IRepository<T> 
{ 
    void Save(T model); 
    void Submit(T model); 
} 

public class Order() 
{ 
    public int Id { get; set; } 
    public string Description { get; set; } 
    public List<AbstractOrderline> OrderLines { get; set; } 

    [XmlIgnore] 
    public IRepository<Order> Repository { get; set; } 

    public void Save() 
    { 
     if (Repository == null) { throw new NotSupportedException(); } 
     Repository.Save(this); 
    } 

    public void Submit() 
    { 
     if (Repository == null) { throw new NotSupportedException(); } 
     Repository.Submit(this); 
    } 
} 

現在,你可以注入thorugh這個倉庫到你的模型中的服務 - 或者客戶特定的邏輯:

// Client-specific implementation 
public class ClientOrderRepository : IRepository<Order> 
{ 
    private readonly IClientOrderService _service; 
    public ClientOrderRepository(IClientOrderService clientOrderService) 
    { 
     _service = clientOrderService; 
    } 

    public void Save(Order order) 
    { 
     _service.Save(order); 
    } 

    public void Submit(Order order) 
    { 
     _service.Submit(order); 
    } 
} 

那麼你的服務和客戶端是這樣的:

// Interface 
Order IOrderService.GetById(int); 

// Service 
Order OrderService.GetById(int id) 
{ 
    return new Order(...); 
} 

// Client 
Order order = IOrderService.GetById(42); 
order.Repository = new ClientRepository(...); 
order.Submit(); 
+1

感謝您使用IClientMessageFormatter的提示。看看我是否可以使用它將對象實例化卸載到DI容器,然後再將它交給解串器。如果這不起作用,我會去構圖路線。 – Toolmaker

+0

使用自定義IClientFormatter/IDispatchFormatter來完成我的初衷實際上是非常有可能的。我不得不放棄Protobuf,而是使用Newtonsoft的Json,因爲protobuf似乎沒有支持DI容器,但是如果我能夠找出WCF層生成的異常,這很有可能。 – Toolmaker

+0

@Toolmaker酷,隨時發佈自己的答案,如果你有它的工作。 – CodeCaster