数据传输对象模式

19

对不起,我是企业应用程序和设计模式的新手。也许这个问题出现了关于设计模式方面的知识不足。我发现使用 DTO 传输数据更好。

我的业务实体类如下:

public class Patient
{    
    public string ID { get; set; }
    public string FullName { get; set; }
    public string FirstName { get; set; }
    public string Surname { get; set; }
}

我的应用程序中,用户只需提供ID和医院ID,因此它会调用另一个Web服务并获取个人信息。

 public class PersonDTO
 {
     public string NIC { get; set; }
     public string FullName { get; set; }
     public string FirstName { get; set; }
     public string BirthPlace { get; set; }
     public string BirthCertificateID { get; set; }
 }

基于这些信息,我将使用DTO模式将其应用于Patient对象。

所以,我想编写一个新的类来将其转换如下。

public class PatientDO
{
    public static Patient ConvertToEntity(
        PatientRegistrationDTO pregDTO,
        PersonDTO person
    )
    {
        Patient p = new Patient();
        p.NIC = pregDTO.NIC;
        p.FullName = person.FullName;
        p.FirstName = person.FirstName;
        return p;
    }
}

但最近我读了一些文章,它们使用了Serializer Helper classXmlSerializer,我不明白为什么要使用这样的东西。

对于DTO模式,是否需要使用XmlSerializer?为什么要使用它?

3个回答

27

你真的应该看一下AutoMapper。

http://automapper.org

这是一个可嵌入你的解决方案中的软件,能够自动地将一个类中的值映射到另一个类中。

它会自动地映射具有相同名称的属性,并且在处理子对象时也相当智能。但是,当需要时它也提供完整的映射控制。

编辑

以下是一些示例,展示了AutoMapper的工作原理。请注意,在现实生活中我不会编写这样的代码。简洁明了!

示例类。

// Common scenario.  Entity classes that have a connection to the DB.
namespace Entities 
{
   public class Manager
   {
      public virtual int Id { get; set; }
      public virtual User User { get; set; }
      public virtual IList<User> Serfs { get; set; }
   }

   public class User
   {
      public virtual int Id { get; set; }
      public virtual string Firstname { get; set; }
      public virtual string Lastname { get; set; }
   }
}



// Model class - bit more flattened
namespace Models 
{
   public class Manager 
   {
      public int Id { get; set; }
      public string UserFirstname { get; set; }
      public string UserLastname { get; set; }
      public string UserMiddlename { get; set; }
   }
}

通常情况下,您需要在项目的某个部分配置所有的AutoMapping。通过我刚才提供的示例,您可以像这样配置Entities.Manager和Models.Manager之间的映射:

// Tell AutoMapper that this conversion is possible
Mapper.CreateMap<Entities.Manager, Models.Manager>();

然后,在您的代码中,您可以使用类似以下内容的语句从实体版本获取新的Models.Manager对象。

// Map the class
var mgr = Map<Entities.Manager, Models.Manager>
  ( repoManager, new Models.Manager() );

顺便提一下,如果你能保持命名的一致性,AM足够聪明地自动解决很多属性问题。

在上面的例子中,如果你将UserFirstname和UserLastname命名一致,它们应该会被自动填充,因为:

  • Manager具有名为User的属性
  • User具有名为Firstname和Lastname的属性

但是,在Entities.Manager和Models.Manager之间进行映射操作后,Models.Manager中的UserMiddlename属性始终为空,因为User没有名为Middlename的公共属性。


8
请注意需要审查性能考虑因素。您可以使用AutoMapper,而不是必须使用它。 - Myrtle
在我看来,AutoMapper最常见的性能问题来自于定义不清晰的映射(由我编写!)。我遇到的最大问题是当AutoMapper爬取nHibernate对象图并进行大量的数据库调用时。 - Paul Alan Taylor
2
这篇文章可能是关于AutoMapper的非常好的阅读材料:http://www.devtrends.co.uk/blog/stop-using-automapper-in-your-data-access-code - masterlopau
不,它并没有提供“完全映射控制”。我曾试图简要使用它,但不得不回到“老派费力的映射例程”。另请参阅https://dev59.com/n3DYa4cB1Zd3GeqPAGsu。 - Dave Van den Eynde
Automapper在传输大量数据库记录时会影响性能。 - Megadotnet
显示剩余2条评论

4
有一个简单易懂的演示在CodeProject网站上,非常值得一看。初学者可以通过它了解设计DTO的基本思路。http://www.codeproject.com/Articles/8824/C-Data-Transfer-Object 数据传输对象(DTO)是一种简单的可序列化对象,用于在应用程序的多个层之间传输数据。DTO中包含的字段通常是原始类型,如字符串、布尔等。其他DTO也可以包含或聚合在DTO中。例如,您可能有一个包含在图书馆DTO中的BookDTOs集合。我创建了一个由多个应用程序使用的框架,该框架利用DTO在各层之间传输数据,还依赖于其他面向对象的模式,如工厂、门面等。与DataSet相比,DTO的一个很大优点是,DTO不必直接匹配数据表或视图。DTO可以从另一个DTO中聚合字段。
这是所有数据传输对象的基类。
using System;

namespace DEMO.Common
{
/// This is the base class for all DataTransferObjects.
    public abstract class DTO
    {
        public DTO()
        {
        }
    }
}

这是一个继承自DTO的派生类:

using System;
using System.Xml.Serialization;
using DEMO.Common;

namespace DEMO.DemoDataTransferObjects
{
public class DemoDTO : DTO
{
    // Variables encapsulated by class (private).
    private string demoId = "";
    private string demoName = "";
    private string demoProgrammer = "";

    public DemoDTO()
    {
    }

    ///Public access to the DemoId field.
    ///String
    [XmlElement(IsNullable=true)]
    public string DemoId
    {
        get
        {
            return this.demoId;
        }
        set
        {
            this.demoId = value;
        }
    }

    ///Public access to the DemoId field.
    ///String
    [XmlElement(IsNullable=true)]
    public string DemoName
    {
        get
        {
            return this.demoName;
        }
        set
        {
            this.demoName = value;
        }
    }

    ///Public access to the DemoId field.
    ///String
    [XmlElement(IsNullable=true)]
    public string DemoProgrammer
    {
        get
        {
            return this.demoProgrammer;
        }
        set
        {
            this.demoProgrammer = value;
        }
    }

}

这是一个DTO的辅助类。它有公共方法来序列化和反序列化DTO。

using System;
using System.Xml.Serialization;
using System.IO;

namespace DEMO.Common
{
public class DTOSerializerHelper
{
    public DTOSerializerHelper()
    {
    }

    /// 
    /// Creates xml string from given dto.
    /// 
    /// DTO
    /// XML
    public static string SerializeDTO(DTO dto)
    {
        try
        {
            XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
            StringWriter sWriter = new StringWriter();
            // Serialize the dto to xml.
            xmlSer.Serialize(sWriter, dto);
            // Return the string of xml.
            return sWriter.ToString();
        }
        catch(Exception ex)
        {
            // Propogate the exception.
            throw ex;
        }
    }

    /// 
    /// Deserializes the xml into a specified data transfer object.
    /// 
    /// string of xml
    /// type of dto
    /// DTO
    public static DTO DeserializeXml(string xml, DTO dto)
    {
        try
        {
            XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
            // Read the XML.
            StringReader sReader = new StringReader(xml);
            // Cast the deserialized xml to the type of dto.
            DTO retDTO = (DTO)xmlSer.Deserialize(sReader);
            // Return the data transfer object.
            return retDTO;
        }
        catch(Exception ex)
        {
            // Propogate the exception.
            throw ex;
        }            
    }

}

现在开始进行序列化/反序列化:
using System;
using DEMO.Common;
using DEMO.DemoDataTransferObjects;

namespace DemoConsoleApplication
{
public class DemoClass
{
    public DemoClass()
    {
    }

    public void StartDemo()
    {
        this.ProcessDemo();
    }

    private void ProcessDemo()
    {
        DemoDTO dto = this.CreateDemoDto();

        // Serialize the dto to xml.
        string strXml = DTOSerializerHelper.SerializeDTO(dto);

        // Write the serialized dto as xml.
        Console.WriteLine("Serialized DTO");
        Console.WriteLine("=======================");
        Console.WriteLine("\r");
        Console.WriteLine(strXml);
        Console.WriteLine("\r");

        // Deserialize the xml to the data transfer object.
        DemoDTO desDto = 
          (DemoDTO) DTOSerializerHelper.DeserializeXml(strXml, 
          new DemoDTO());

        // Write the deserialized dto values.
        Console.WriteLine("Deseralized DTO");
        Console.WriteLine("=======================");
        Console.WriteLine("\r");
        Console.WriteLine("DemoId         : " + desDto.DemoId);
        Console.WriteLine("Demo Name      : " + desDto.DemoName);
        Console.WriteLine("Demo Programmer: " + desDto.DemoProgrammer);
        Console.WriteLine("\r");
    }

    private DemoDTO CreateDemoDto()
    {
        DemoDTO dto = new DemoDTO();

        dto.DemoId            = "1";
        dto.DemoName        = "Data Transfer Object Demonstration Program";
        dto.DemoProgrammer    = "Kenny Young";

        return dto;
    }
}

最后,这段代码将在主应用程序中执行。
static void Main(string[] args)
{
    DemoClass dc = new DemoClass();
    dc.StartDemo();
}

2
一个XmlSerializer或JsonSerializer可以用于从源(webservice)序列化(加载)XML或Json数据。或者解释DTO的名称:您将数据从源(webservice)序列化(传输)到(通用DTO)对象中。因此,DTO是通用对象。有时候最好制作一个尽可能广泛的DTO对象,并完全填充它,这样您就可以从该对象中使用任何您喜欢的内容,并将其复制到您自己的程序对象中。
例如:我开发了一个用于显示运输导航数据的程序。我将整个xml或json消息序列化到DTO对象中。在这个DTO对象中,比我在我的程序中需要的信息更多,而且它可能以不同的形式存在,所以我只使用需要的部分。 DTO对象使从源(webservices)提取数据更加容易。
我不想使用AutoMapper,因为它含有“自动”一词。我想知道我正在做什么,并考虑我的数据将去哪里。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接