我正在编写一个使用内部基于WCF的REST服务的应用程序,我承认自己是一名REST新手。由于我无法使用“添加服务引用”,因此我没有现成的代理对象来表示服务方法中的返回类型。到目前为止,我能够使用该服务的唯一方法是共享包含服务公开的数据类型的程序集。
我对这个安排的问题是我只看到两种可能性:
实施DTO(DataContracts)并从我的服务中公开这些类型。我仍然需要共享一个程序集,但这种方法会将程序集中包含的类型限制为服务契约和DTO。我不喜欢仅仅为了使用它们而使用DTO,尽管它们添加了另一层抽象和处理时间以从域对象转换为DTO,反之亦然。另外,如果我想在客户端上拥有业务规则,验证等,我还是必须共享域对象,因此增加了必要的复杂性。
支持我的域对象的序列化,公开这些类型并共享该程序集。这将允许我与客户共享业务和验证逻辑,但它也将我的域对象的一部分暴露给客户端,仅用于服务应用程序。
也许一个例子可以帮助讨论......
我的客户端应用程序将显示从REST服务获取的文档列表(GET操作)。该服务返回一个DocumentInfo对象数组(文档的轻量级,只读表示)。
当用户选择其中一个项目时,客户端从REST服务(GET by id)检索完整的Document对象并显示数据输入表单,以便用户可以修改该对象。我们希望验证规则能够提供丰富的用户体验。
当用户提交更改时,Document对象将提交给REST服务(PUT操作),并将其保存到后端数据存储。
如果文档的状态允许,用户可以“发布”文档。在这种情况下,客户端使用Document.ID值向REST服务发出请求,服务通过检索服务器端Document域对象并调用Publish方法来执行操作。发布方法不应对客户端应用程序可用。
在我看来,我的Document和DocumentInfo对象必须在共享程序集中。这样做可以使客户端使用Document.Publish。隐藏它的一个想法是使方法内部并添加一个InternalsVisibleTo属性,允许我的服务应用程序调用方法而不是客户端,但这似乎“很臭。”
我是在正确的轨道上还是完全错过了什么?
答案 0 :(得分:4)
您在服务器上使用的类不应与您在客户端上使用的类相同(除了在数据传输过程中)。最好的方法是创建一个包含DTO的包(程序集/项目),并在服务器和客户端之间共享它们。你确实提到你不想为了它而创建DTO的 ,但这是最好的做法。添加额外层的性能影响可以忽略不计,分层实际上有助于使您的应用程序更易于开发和维护(避免像客户端访问服务器代码那样的情况)。
我建议从以下软件包开始:
您的客户端应用程序代码应调用Repository来获取Model对象(如果您不确定如何解决此问题,也可以考虑查看MVVM。)
如果您的服务代码足够复杂,需要访问Model类,您应该创建一个单独的Model包(显然给它一个不同的名称) - 服务器和客户端上应该存在的唯一类是DTO类。 / p>
答案 1 :(得分:2)
我认为我会发布我采用的方法,同时给予Greg和Jake以帮助引导我走上正轨。
虽然Jake是正确的,只要它实现相同的数据协定,就可以使用任何类型对客户端上的数据进行反序列化,但是在没有WSDL的情况下强制执行此操作可能有点棘手。我所处的环境中,其他开发人员将使用我的解决方案来支持和维护现有的以及创建使用我的服务的新客户端。它们用于“添加服务参考”并继续。
Greg关于在客户端和服务器上使用不同对象的观点是最有帮助的。我试图通过在客户端和服务器之间共享我的域层来最小化重复,这是我困惑的根源。一旦我将它们分成两个不同的应用程序并单独查看它们,每个应用程序都有自己的用例,图片就变得更加清晰了。
因此,我现在正在共享一个包含我的服务合同的Contracts程序集,以便客户端可以轻松地创建到服务器的通道(在客户端使用WCF)和表示在客户端和客户端之间传递的DTO的数据协定。服务。
在客户端上,我有ViewModel对象,它们为UI包装Model对象(数据协定),并使用服务代理类使用共享程序集中的服务契约与服务进行通信。因此,当用户单击UI中的“发布”按钮时,控制器(或WPF / SL中的命令)调用服务代理上的Publish方法,传递要发布的文档的ID。服务代理将请求中继到REST API(发布操作)。
在服务器上,REST API使用相同的服务合同实现。在这种情况下,该服务与我的域服务,存储库和域对象一起执行任务。因此,当调用发布服务操作时,服务从DocumentRepository检索Document域对象,在对象上调用Publish方法,该方法更新对象的内部状态,然后服务将更新的对象传递给存储库的Update方法坚持改变。
我对结果很满意,因为我相信这给了我一个更强大和可扩展的架构。我可以根据需要更改ViewModel以支持UI,而无需考虑对服务进行策略,同样可以更改服务操作(域层)的内部实现,而不会影响客户端应用程序。所有这两个都是他们共享的合同。很干净。
答案 2 :(得分:0)
您可以序列化您的域对象,然后在客户端上将它们反序列化为不同的类型。两种类型都需要实现相同的数据协定。所有可序列化类型至少具有默认数据协定,其中包括所有公共读/写属性和字段。