具有依赖注入的 WCF 数据契约设计
因此,我有一个分层应用程序,我在其上添加了 WCF 服务接口。该服务只是一个外观,其中所有业务逻辑都已存在于业务逻辑层 (BLL) 内的业务对象 (BO) 中,业务逻辑层 (BLL) 是一个类库。在 BLL 中,我们使用构造函数注入将依赖项注入到 BO 中。这一切都需要良好的单元测试等。关于问题...
通常,我只需为每个服务方法创建一组请求/响应对象作为数据契约,并具有适合操作的属性。如果操作需要将我们的“实体”之一传递给方法或从方法传递,我只需定义该类型的属性,一切都会好起来的(我们所有的 BO 都是可序列化的)。然而,当这些“实体”之一被传递到服务方法中时,WCF 会反序列化该对象,而不会调用我们定义的构造函数,因此,依赖关系不会解析。
让我们以名为 CreateSomething 的服务方法为例。我通常将其定义为具有如下签名的服务操作:
CreateSomethingResponse CreateSomething(CreateSomethingRequest request);
CreateSomethingRequest 将是一个 DataContract,并且在其属性中具有表示“实体”的 Something 类型的属性被传递到服务中。在本例中,Something 是一个业务对象,在实例化时期望从 DI 容器接收 ISomethingRepository 接口的实例 - 正如我上面所说,它不会当 WCF 反序列化服务器上的对象时会发生这种情况。
选项 #2 是从 DataContract 中删除 Something 属性,并在我的 DataContract 中显式定义每个属性,然后在我的服务方法内创建 Something 类的新实例,让容器注入依赖项,然后将属性值从 DataContract 对象映射到 BO。我当然可以做到这一点,但我担心现在有两个地方可以进行更改,例如,如果我想向 Something 类型添加一个属性。而且,由于属性很多,因此存在大量代码重复。
有没有人跨过这座桥,如果是的话,您能分享一下您的想法以及您在自己的应用程序中如何处理或将如何处理这种情况吗?谢谢!!!
So I have a layered application that I am adding a WCF service interface on top of. The service is simply a facade with all of our business logic already existing in Business Objects (BOs) within the Business Logic Layer (BLL) which is a class library. Within the BLL we use constructor injection to inject dependencies into the BOs. This is all working with good unit testing, etc. On to the problem...
Ordinarily I'd simply create a set of Request/Response objects as DataContracts for each service method with the appropriate properties for the operation. If the operation required one of our "entities" to be passed either to or from the method, I'd simply define a property of that type and everything would be fine (all of our BOs are serializable). However when one of these "entities" is passed into a service method, WCF deserializes the object without ever invoking the constructors we've defined and, as a result, the dependencies don't resolve.
Let's use the case of a service method called CreateSomething. I'd normally define this as a service operation with a signature like:
CreateSomethingResponse CreateSomething(CreateSomethingRequest request);
CreateSomethingRequest would be a DataContract and have amongst its properties a property of type Something that represented the "entity" being passed into the service. Something, in this case, is a business object that expects to receive an instance of the ISomethingRepository interface from the DI container when instantiated - which, as I said above, does not happen when WCF deserializes the object on the server.
Option #2 is to remove the Something property from the DataContract and define each of the properties explicitly in my DataContract then inside my service method, create a new instance of the Something class, letting the container inject the dependency, then map the property values from the DataContract object into the BO. And I can certainly do that but I am concerned about now having two places to make changes if, say, I want to add a property to the Something type. And, with a lot of properties, there's a lot of code duplication.
Has anyone crossed this bridge and, if so, can you share your thoughts and how you have or would approach this situation in your own applications? Thx!!!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
关于您的问题有两个答案:
第一:不要发送您的实体并使用数据传输对象。您的实体是具有逻辑和数据的业务对象。业务对象的逻辑很可能用于控制数据。因此,让业务对象在业务层控制其数据并仅交换虚拟箱子。
第二:如果您不想遵循第一种方法,请检查 IoC 容器的文档。通常有两种方法来解决依赖关系。例如,Unity 提供:
Resolve
- 构建新实例并注入所有依赖项(构造函数注入所需)BuildUp
- 获取现有实例并解析所有属性依赖项。这应该是你的选择。There are two answers on your problem:
First: Do not send your entities and use data transfer objects instead. Your entities are business objects with its logic and data. The logic of business objects is most probably used to control the data. So let the business object control its data in business layer and exchange only dummy crates.
Second: If you don't want to follow the first approach, check documentation of your IoC container. There are ususally two methods for resolving dependencies. For example Unity offers:
Resolve
- builds new instance and injects all dependencies (necessary for constructor injection)BuildUp
- takes existing instance and resolves all property dependencies. This should be your choice.谢谢拉迪斯拉夫的回答,因为你证实了我脑海中已经存在的内容。
我最终做的是稍微改变我的方法。我意识到我对业务对象的使用本身是矫枉过正且不必要的。或者也许,只是误导了。在评估我的要求时,我意识到我可以“简化”我的方法并使一切正常。通过采用应用程序中的每个逻辑层并查看各层之间需要传递哪些数据,我找到了一种可行的设计。
首先,对于我的业务逻辑层,我实现了一个工作单元对象:SomethingManager,而不是业务对象。 SomethingManager 与我的根 Something 实体绑定,因此我想要对 Something 执行的任何操作都是通过 SomethingManager< 完成的/强>。这包括 GetById、GetAll、Save 和 Delete 等方法。
SomethingManager 类在其构造函数中接受两个对象:IValidator 和 ISomethingRepository。这些将由 IoC 容器注入。前者让我可以使用我们选择的任何框架(最初是验证应用程序块)执行所有必要的验证,而后者让我对持久性一无所知,并抽象了当今 Linq-to-SQL 的使用,并使以后升级到 EF4 变得更加容易。
对于我的服务层,我已将 IoC 容器(在本例中为 Unity)连接到 WCF,以便容器创建服务实例。这允许我将 ISomethingManager 实例注入到我的服务中。使用该接口,我可以打破依赖关系并轻松地对服务类进行单元测试。另外,由于容器正在注入 ISomethingManager 实例,因此它正在构建它并将自动解析它的依赖项。
然后,我创建了 DataContracts 来表示数据在通过服务进行网络传输时应如何显示。每个请求/响应对象都包含这些数据契约作为数据成员,而不是直接引用我的实体类(或 BO)。由服务方法来映射来自或去往业务逻辑层的数据(通过ISomethingManager) - 使用 AutoMapper 使这一过程变得干净和高效。
回到数据层,我通过定义实现 BLL 所需接口的分部类来简单地扩展了生成的实体类。例如,Something L2S 实体有一个实现ISomething 的部分定义。 ISomething 是 SomethingManager (和 ISomethingManager 接口)和 ISomethingRepository 的工作对象,使得查询数据库并将 L2S 实体沿链向上传递,以供服务层使用和传递(服务层无需了解或依赖于 L2S 实现)。
我感谢任何人对这种方法提出的任何评论、问题、批评或建议。
Thanks, Ladislav, for your answer as you confirmed what was already in my head.
What I ended up doing was to change my approach a little. I realized that my use of a business object, per se, was overkill and unnecessary. Or perhaps, just misdirected. When evaluating my requirements, I realized that I could "simplify" my approach and make everything work. By taking each logical layer in my application and looking at what data needed to pass between the layers, I found a design that works.
First, for my business logic layer, instead of a business object, I implemented a Unit of Work object: SomethingManager. SomethingManager is tied to my root Something entity so that any action I want to perform on or with Something is done through the SomethingManager. This includes methods like GetById, GetAll, Save and Delete.
The SomethingManager class accepts two objects in its constructor: an IValidator<Something> and an ISomethingRepository. These will be injected in by the IoC container. The former lets me perform all of the necessary validation using whatever framework we chose (initially the Validation Application Block) and the latter gives me persistance ignorance and abstracts the use of Linq-to-SQL today and makes upgrading to EF4 much easier later on.
For my service layer, I've wired the IoC container (Unity in this case) into WCF so the service instance is created by the container. This allows me to inject an instance of ISomethingManager into my service. Using the interface, I can break the dependency and easily unit test the service class. Plus, because the container is injecting the ISomethingManager instance, it is constructing it and will automatically resolve it's dependencies.
I then created DataContracts to represent how the data should appear when transferred across the wire via the service. Each Request/Response object contains these DataContracts as DataMembers rather than referencing my entity classes (or BOs) directly. It is up to the service method to map the data coming from or going to the Business Logic Layer (via ISomethingManager) - using AutoMapper to make this clean and efficient.
Back in the data layer, I've simply extended the generated entity classes by defining a partial class that implements the desired interface from the BLL. For instance, the Something L2S entity has a partial defined that implements ISomething. And ISomething is what the SomethingManager (and ISomethingManager interface) and ISomethingRepository work with making it very easy to query the database and pass the L2S entity up the chain for the service layer to consume and pass on (without the service layer having any knowledge or dependency on the L2S implementation).
I appreciate any comment, questions, criticisms or suggestions anyone has on this approach.