共享数据合同的子类型

时间:2012-03-27 14:33:51

标签: wcf

根据互联网上有关服务引用的人的建议,我现在摆脱它们并将服务/数据合同拆分为服务器和客户端都可访问的公共程序集。总的来说,这似乎工作得很好。

但是,当我尝试在服务中使用自定义对象或自定义子类型时,我遇到了问题。最初我想只在公共程序集中定义接口作为数据的合同。我很快就知道这不会起作用,因为客户端需要一个具体的类来在从服务接收对象时实例化对象。所以相反,我使用了一个简单的类,基本上是这样的:

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

然后在服务合同(接口)中,我有一个返回这样一个对象的方法。

现在如果我只是在服务实现中创建这样一个对象并返回它,它就可以了。但是我想在服务(或底层业务逻辑)中定义它的子类型,它定义了一些更多的东西(例如数据库访问的方法,或者只是一些处理对象的方法)。

为简单起见,子类型如下所示:

// (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类型进行反序列化;或者解决方案是否仍然使用数据传输对象?

1 个答案:

答案 0 :(得分:1)

正如@Sixto Saez指出的那样,继承和WCF往往不能很好地结合在一起。原因是继承属于OO世界,而不是传递消息的世界。

话虽如此,如果您控制服务的两端,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>();
    }
}

使用这种技术,您可以有效地反转依赖性。

Registry是一个简单的类,它允许其他程序集在​​运行时将类型注册为指定基类的子类型。当应用程序自行引导时,可以执行此任务,例如,可以通过反映包含子类型的程序集中的类型来完成此任务。

这实现了允许正确处理子类型的目标,而不需要TestObject程序集需要对子类型程序集进行引用。

我在“闭环”应用程序中成功使用了这种技术,其中客户端和服务器都受到控制。您应该注意到这种技术有点慢,因为在串行化/反序列化时,必须在两端重复调用GetKnownTestObjects方法。但是,如果您准备好承受这种轻微的缺点,那么使用WCF提供通用Web服务是一种相当简洁的方法。它还消除了指定实际类型的所有'KnownTypeAttributes'的需要。