假设我有以下课程:
class Camera
{
public Camera(
double exposure,
double brightness,
double contrast,
RegionOfInterest regionOfInterest)
{
this.exposure = exposure;
this.brightness = brightness;
this.contrast = contrast;
this.regionOfInterest = regionOfInterest;
}
public void ConfigureAcquisitionFifo(IAcquisitionFifo acquisitionFifo)
{
// do stuff to the acquisition FIFO
}
readonly double exposure;
readonly double brightness;
readonly double contrast;
readonly RegionOfInterest regionOfInterest;
}
...和DTO在服务边界(WCF)上传输摄像头信息,比如在WinForms / WPF / Web应用程序中查看:
using System.Runtime.Serialization;
[DataContract]
public class CameraData
{
[DataMember]
public double Exposure { get; set; }
[DataMember]
public double Brightness { get; set; }
[DataMember]
public double Contrast { get; set; }
[DataMember]
public RegionOfInterestData RegionOfInterest { get; set; }
}
现在我可以向Camera
添加一个方法来公开其数据:
class Camera
{
// blah blah
public CameraData ToData()
{
var regionOfInterestData = regionOfInterest.ToData();
return new CameraData()
{
Exposure = exposure,
Brightness = brightness,
Contrast = contrast,
RegionOfInterest = regionOfInterestData
};
}
}
或,我可以创建一个方法,需要传入一个特殊的IReporter,让Camera公开其数据。这将消除对Contracts图层的依赖性(Camera不再需要了解CameraData):
class Camera
{
// beep beep I'm a jeep
public void ExposeToReporter(IReporter reporter)
{
reporter.GetCameraInfo(exposure, brightness, contrast, regionOfInterest);
}
}
那我该怎么办?我更喜欢第二个,但它需要IReporter有一个CameraData字段(由GetCameraInfo()
改变),这感觉很奇怪。此外,如果有更好的解决方案,请与我分享!我仍然是一个面向对象的新手。
答案 0 :(得分:13)
我通常会说 no ,它们不应该,因为DTO特定于服务或应用程序,而域模型是您的“最内层”层,并且应该没有依赖关系。 DTO是其他的实现细节,而不是域模型,因此,它打破了域模型的抽象,了解它们。
您是否考虑过查看AutoMapper?你最终会以这种方式编写更少的代码。在这种情况下,我认为你可以简单地逃脱:
Mapper.CreateMap<RegionOfInterest, RegionOfInterestData>();
Mapper.CreateMap<Camera, CameraData>();
后来:
CameraData cd = Mapper.Map<Camera, CameraData>(camera);
这不仅减少了代码流失,而且将映射代码划分到自己的“映射层”中 - 您有一个或多个模块可以注册这些映射,您可以将它们放在真正使用DTO的任何程序集中。
当然,您始终可以创建扩展方法来简化实际映射:
public static class CameraExtensions
{
public static CameraData ToCameraData(this Camera camera)
{
return Mapper.Map<Camera, CameraData>(camera);
}
}
这使整个事情变得如编写camera.ToCameraData()
一样简单,但没有在域对象(Camera
)和DTO({{1}之间创建硬依赖关系})。你基本上拥有原始版本的所有易用性,但没有耦合。
如果您正在创建这些依赖项,因为您尝试从私有CameraData
数据创建CameraData
对象,而这些数据未公开曝光,那么我的立即反应是,某些内容不太正确这个设计。为什么不在Camera
对象上公开只读属性?无论如何,如果你通过Camera
方法让外界访问它们,那么你显然没有隐藏这些信息,你只是让它变得更加麻烦。
如果您决定在未来3个月内需要不同类型的DTO,该怎么办?每次要支持新的用例时,都不必修改以域为中心的ToData
对象。在我看来,更好的是在类中放置一些只读的公共属性,以便映射器可以访问他们需要的属性。
答案 1 :(得分:4)
我通常这样做:业务对象在业务层DLL中是“纯粹的”。然后,我在WCF层添加一个Camera.MapToCameraDataContract扩展方法。我也特别在服务层上有反向扩展方法(CameraDataContract.MapToCamera)。
所以从本质上讲,我是第一种方式,但ToData方法是只有WCF层知道的扩展方法。
答案 2 :(得分:0)
第一个(暴露DTO)比我更好。它更简单,运行速度更快,更易于理解和维护。由于DTO确实不依赖于数据库对象,因此它仍然可以轻松实现减少依赖性的目标。
答案 3 :(得分:0)
我在我的DTO上自行设置了方法:
[DataContract]
public class CameraData
{
...
public Camera ToCamera() { ... }
public static CameraData FromCamera(Camera c) { ... }
}
这样我的域对象就不必知道我的DTO了。
答案 4 :(得分:0)
您的服务WCF是否为WCF?
如果是,那么您可以选择将业务对象用作DTO(只要您的业务对象是持久性无知的)。如果您这样做,我建议您将CameraData类更改为ICameraData接口,并使Camera实现ICameraData。保持接口上的属性(DataContract等)。
然后,您可以将业务对象从客户端传递到服务器。请注意特定客户端或服务器端的任何逻辑。
我blog post中的第一张图片显示了重新使用业务对象对象客户端是多么容易(对话框是“添加服务引用”时显示的内容)。这篇博文有一些重新使用商业对象的信息。
我无法告诉您要使用ExposeToReporter实现什么,但是你是对的,它看起来不对,我个人在IReporter上放了一个采用ICameraData参数的方法,然后在记者身上设置细节在那之内。
这个东西的一个很好的学习来源是dnrtv。在标题中观看WCF的所有内容,尤其是Miguel Castro的Extreme WCF!