共享程序集是从 WCF REST 服务创建对象的唯一方法吗
我正在编写一个使用基于 WCF 的内部 REST 服务的应用程序,我承认自己是 REST 新手。由于我无法使用“添加服务引用”,因此我没有现成的代理对象来表示服务方法的返回类型。到目前为止,我能够使用该服务的唯一方法是共享包含该服务公开的数据类型的程序集。
我对这种安排的问题是,我只看到两种可能性:
实现 DTO(数据契约)并从我的服务中公开这些类型。我仍然需要共享程序集,但这种方法会将程序集中包含的类型限制为服务合同和 DTO。我不喜欢仅仅为了使用 DTO 而使用它们,尽管它们添加了另一层抽象和处理时间来从域对象转换为 DTO,反之亦然。另外,如果我想在客户端上拥有业务规则、验证等,无论如何我都必须共享域对象,因此增加了必要的复杂性。
支持我的域对象的序列化,公开这些类型并共享该程序集。这将允许我与客户端共享业务和验证逻辑,但它也会向客户端公开仅适用于服务应用程序的部分域对象。
也许一个示例有助于讨论...
我的客户端应用程序将显示从 REST 服务(GET 操作)获取的文档列表。该服务返回一个 DocumentInfo 对象数组(文档的轻量级只读表示)。
当用户选择其中一项时,客户端会从 REST 服务(通过 id 获取)检索完整的文档对象,并显示一个数据输入表单,以便用户可以修改该对象。我们希望验证规则能够提供丰富的用户体验。
当用户提交更改时,Document 对象将提交到 REST 服务(PUT 操作),并在其中持久保存到后端数据存储。
如果文档的状态允许,用户可以“发布”文档。在这种情况下,客户端使用 Document.ID 值向 REST 服务 POST 请求,该服务通过检索服务器端 Document 域对象并调用 Publish 方法来执行操作。 Publish 方法不应对客户端应用程序可用。
在我看来,我的 Document 和 DocumentInfo 对象必须位于共享程序集中。这样做使 Document.Publish 对客户端可用。隐藏它的一个想法是使该方法成为内部方法并添加一个 InternalsVisibleTo 属性,该属性允许我的服务应用程序调用该方法而不是客户端,但这看起来“很臭”。
我走在正确的轨道上还是完全错过了一些东西?
I am writing an application that is consuming an in-house WCF-based REST service and I'll admit to being a REST newbie. Since I can't use the "Add Service Reference", I don't have ready-made proxy objects representing the return types from the service methods. So far the only way I've been able to work with the service is by sharing the assembly containing the data types exposed by the service.
My problem with this arrangment is that I see only two possibilities:
Implement DTOs (DataContracts) and expose those types from my service. I would still have to share an assembly but this approach would limit the types contained in the assembly to the service contract and DTOs. I don't like to use DTOs just for the sake of using them, though as they add another layer of abstraction and processing time to convert from domain object to DTO and vice versa. Plus, if I want to have business rules, validation, etc. on the client, I'd have to share the domain objects anyways, so is the added complexity necessary.
Support serialization of my domain objects, expose those types and share that assembly. This would allow me to share business and validation logic with the client but it also exposes parts of my domain objects to the client that are meant only for the service app.
Perhaps an example would help the discussion...
My client application will display a list of documents that is obtained from the REST service (a GET operation). The service returns an array of DocumentInfo objects (lightweight, read-only representation of a Document).
When the user selects one of the items, the client retrieves the full Document object from the REST service (GET by id) and displays a data entry form so the user can modify the object. We would want validation rules for a rich user experience.
When the user commits the changes, the Document object is submitted to the REST service (a PUT operation) where it is persisted to the back-end data store.
If the state of the Document allows, the user may "Publish" the Document. In this case, the client POSTs a request to the REST service with the Document.ID value and the service performs the operation by retrieving the server-side Document domain object and calling the Publish method. The Publish method should not be available to the client application.
As I see it, my Document and DocumentInfo objects would have to be in a shared assembly. Doing this makes Document.Publish available to the client. One idea to hide it would be to make the method internal and add an InternalsVisibleTo attribute that allows my service app to call the method and not the client but this seems "smelly."
Am I on the right track or completely missing something?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您在服务器上使用的类不应与您在客户端上使用的类相同(除了数据传输本身期间)。最好的方法是创建一个包含 DTO 的包(程序集/项目),并在服务器和客户端之间共享它们。您确实提到您不想为此而创建 DTO,但这是最佳实践。添加额外层对性能的影响可以忽略不计,分层实际上有助于使您的应用程序更易于开发和维护(避免像您这样的客户端可以访问服务器代码的情况)。
我建议从以下包开始:
您的客户端应用程序代码应该调用存储库来获取模型对象(如果您不确定如何执行此操作,您也可以考虑查看 MVVM)。
如果您的服务代码足够复杂以至于需要访问模型类,则您应该创建一个单独的模型包(显然给它一个不同的名称) - 唯一应该同时存在于服务器和客户端上的类是 DTO 类。
The classes you use on the server should not be the same classes you use on the client (apart from during the data transfer itself). The best approach is to create a package (assembly/project) containing DTOs, and share these between the server and the client. You did mention that you don't want to create DTO's for the sake of it, but it is best practice. The performance impact of adding extra layers is negligible, and layering actually helps make your application easier to develop and maintain (avoiding situations like yours where the client has access to server code).
I suggest starting with the following packages:
Your client application code should call into Repository to get Model objects (you might also consider looking into MVVM if your not sure how to go about this).
If your service code is sufficiently complex that it needs access to Model classes, you should create a separate Model package (obviously give it a different name) - the only classes which should exist both on server and client are DTO classes.
我想我应该发布我所采取的方法,同时感谢格雷格和杰克帮助指导我走上这条路。
虽然 Jake 认为只要实现相同的数据协定,就可以使用任何类型在客户端上反序列化数据,这是正确的,但在没有 WSDL 的情况下强制执行此操作可能会有点棘手。在我所处的环境中,其他开发人员将使用我的解决方案来支持和维护现有解决方案,并创建使用我的服务的新客户端。它们用于“添加服务引用”并进行。
Greg 关于在客户端和服务器上使用不同对象的观点是最有帮助的。我试图通过在客户端和服务器之间共享域层来最大程度地减少重复,这是我困惑的根源。当我将它们分成两个不同的应用程序并单独查看它们(每个应用程序都有自己的用例)时,情况就变得更加清晰。
因此,我现在共享一个包含我的服务契约的 Contracts 程序集,以便客户端可以轻松创建到服务器的通道(在客户端使用 WCF)以及表示客户端和服务之间传递的 DTO 的数据契约。
在客户端上,我有 ViewModel 对象,它包装 UI 的模型对象(数据协定),并使用服务代理类通过共享程序集中的服务协定与服务进行通信。因此,当用户单击 UI 中的“发布”按钮时,控制器(或 WPF/SL 中的命令)将调用服务代理上的 Publish 方法,并传入要发布的文档的 ID。服务代理将请求中继到 REST API(发布操作)。
在服务器上,REST API 是使用相同的服务契约来实现的。在这种情况下,该服务与我的域服务、存储库和域对象一起执行任务。因此,当调用 Publish 服务操作时,服务会从 DocumentRepository 检索 Document 域对象,调用该对象的 Publish 方法来更新该对象的内部状态,然后该服务将更新后的对象传递给存储库的 Update 方法坚持改变。
我对结果很满意,因为我相信这为我提供了一个更强大、更可扩展的架构。我可以根据需要更改 ViewModel 以支持 UI,而不必担心污染服务,同样,可以更改服务操作(域层)的内部实现,而不会影响客户端应用程序。约束两人的唯一因素是他们共享的合同。相当干净。
I thought that I'd post the approach I took while giving credit to both Greg and Jake for helping guide me down the path.
While Jake is correct that deserializing the data on the client can be done with any type as long as it implements the same data contract, enforcing this without WSDL can be a bit tricky. I'm in an environment where other developers will be working with my solution both to support and maintain the existing as well as creating new clients that consume my service. They are used to "Add Service Reference" and going.
Greg's points about using different objects on the client and the server were the most helpful. I was trying to minimize duplicate by sharing my domain layer between the client and the server and that was the root of my confusion. As soon as I separated these into two distinct applications and looked at them in isolation, each with their own use cases, the picture became clearer.
As a result, I am now sharing a Contracts assembly which contains my service contracts so that a client can easily create a channel to the server (using WCF on the client-side) and data contracts representing the DTOs passed between client and service.
On the client, I have ViewModel objects which wrap the Model objects (data contracts) for the UI and use a service agent class to communicate with the service using the service contracts from the shared assembly. So when the user clicks the "Publish" button in the UI, the controller (or command in WPF/SL) calls the Publish method on the service agent passing in the ID of the document to publish. The service agent relays the request to the REST API (Publish operation).
On the server, the REST API is implemented using the same service contracts. In this case, the service works with my domain services, repositories and domain objects to carry out the tasks. So when the Publish service operation is invoked, the service retrieves the Document domain object from the DocumentRepository, calls the Publish method on the object which updates the internal state of the object and then the service passes the updated object to the Update method of the repository to persist the changes.
I am pleased with the outcome as I believe this gives me a more robust and extensible architecture to work with. I can change the ViewModels as needed to support the UI with no concern over poluting the service(s) and, likewise, change the internal implementation of the service operations (domain layer) without affecting the client application(s). All that binds the two are the contracts they share. Pretty clean.
您可以序列化域对象,然后在客户端将它们反序列化为不同类型。两种类型都需要实现相同的数据契约。所有可序列化类型至少有一个默认数据协定,其中包括所有公共读/写属性和字段。
You can serialize your domain objects and then de-serialize them into different types on the client. Both types need to implement the same data contract. All serializable types have at least a default data contract that includes all public read/write properties and fields.