2012-03-27 58 views
0

根據互聯網上有關服務引用的人的建議,我現在擺脫了它們,並將服務/數據合約拆分爲可由服務器和客戶端訪問的通用程序集。總的來說,這似乎工作得很好。共享數據合同的子類型

然而,我試圖在服務中使用自定義對象或自定義子類型時遇到問題。起初我只想將通用程序集中的接口定義爲數據的契約。我很快就知道這不會起作用,因爲客戶端需要一個具體的類來從服務接收對象時實例化對象。所以代替我用一個簡單的類代替,基本上是這樣的:

// (defined in the common assembly) 
public class TestObject 
{ 
    public string Value { get; set; } 
} 

然後在服務合同(接口),I有一個返回這樣的對象的方法。

現在,如果我只是在服務實現中創建這樣一個對象並返回它,它就可以工作。不過,我想在服務(或底層業務邏輯)中定義它的一個子類型,它定義了更多的東西(例如數據庫訪問的方法,或者只是一些對這些對象有效的方法)。

所以爲了簡單起見,亞型看起來是這樣的:

// (defined on the server) 
public class DbTestObject : TestObject 
{ 
    public string Value { get; set; } 
    public DbTestObject(string val) 
    { 
     Value = val; 
    } 
} 

而且在服務上,而不是創建一個TestObject,我創建的亞型,並將其返回:

public TestObject GetTestObject() 
{ 
    return new DbTestObject("foobar"); 
} 

如果我運行這現在,並使客戶端電話GetTestObject,然後我立即得到一個CommunicationException與以下錯誤文本:「套接字連接被中止。這可能是由處理您的消息時出錯或遠程主機超出接收超時或基礎網絡資源問題引起的。本地套接字超時時間爲'00:09:59.9380000'。「

我已經發現,原因是客戶端不知道如何反序列化DbTestObject。一種解決方案是使用KnownTypeAttribute聲明基類型以使其知道子類型。但是這需要將子類型轉移到常用程序集中,這當然是我想要避免的,因爲我希望邏輯與客戶端分離。

有沒有辦法告訴客戶只使用TestObject類型進行反序列化;或者解決方案是否會使用數據傳輸對象?

+0

如果您在使用基於消息的WCF框架時嘗試應用OO概念(如繼承),那麼您正在游泳。使用DTO通常是在使用WCF服務傳輸數據時的最佳實踐,它們更貼近消息傳遞隱喻。 – 2012-03-27 16:56:18

+0

@SixtoSaez我也這麼認爲,但是當你以前沒有去過它時很難正確地進入它::/ – poke 2012-03-27 19:49:14

+0

嚴格地說,如果你實現一個[自定義序列化擴展],你可以控制WCF如何序列化你的對象類型, (http://code.msdn.microsoft.com/windowsdesktop/WCF-Custom-Serialization-43b3ee7a)用於實例化您的類型實例。我不知道如何描述自定義序列化器的繼承鏈(可能通過屬性),但它應該是可能的。同樣,你強迫繼承消息交換模式,但如果意志堅強...... :) – 2012-03-27 20:16:29

回答

1

正如@Sixto Saez指出的,繼承和WCF不會很好地融合在一起。原因在於繼承屬於面向對象的世界,而不是消息傳遞的世界。儘管如此,如果您在控制服務的兩端,KnownType允許您擺脫消息傳遞的限制並利用繼承的好處。爲了避免取得依賴關係,可以利用KnownTypeAttribute獲取方法名稱的能力,而不是類型參數。這允許您在運行時動態指定已知類型。

E.g.

[KnownType("GetKnownTestObjects")] 
[DataContract] 
public class TestObject 
{ 
    [DataMember] 
    public string Value { get; set; } 

    public static IEnumerable<Type> GetKnownTestObjects() 
    { 
     return Registry.GetKnown<TestObject>(); 
    } 
} 

使用這種技術,可以有效地反轉依賴關係。

註冊表是一個簡單的類,允許其他程序集在​​運行時將類型註冊爲指定基類的子類型。當應用程序自引導並且希望完成時,可以執行此任務,例如,通過反映包含子類型的程序集中的類型。

這實現了您的目標,即允許在沒有TestObject程序集需要對子類型程序集引用的情況下正確處理子類型。

我已經成功地在客戶端和服務器都受到控制的'閉環'應用中成功地使用了這種技術。你應該注意到這個技術有點慢,因爲調用GetKnownTestObjects方法必須在序列化/反序列化兩端重複進行。但是,如果您準備好應對這種輕微的負面影響,那麼使用WCF提供通用Web服務是一種相當乾淨的方式。它也消除了所有那些指定實際類型的「KnownTypeAttributes」的需求。

+0

這很酷,謝謝你。但是它並不能真正解決我的問題,因爲它仍然需要客戶端知道服務器的子類型(即客戶端需要在註冊表中註冊服務器的類型)。無論如何,我會接受這個答案,因爲它肯定有幫助,而且我也不希望我的問題有更好的解決方案。我想從長遠來看,最好是定義常見的DTO。 – poke 2012-03-28 10:46:17

+0

是的,它確實需要客戶端知道子類型 - 服務器和客戶端都必須在每端都填充自己的註冊表。 DTO可能是一個不錯的選擇。另一種可能性是尋求合成解決方案(而不是繼承)。如果服務器類型沒有額外的數據需要返回到基本類型中包含的數據以外,則此方法可能有效。 – 2012-03-28 11:41:37