2012-03-27 123 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