Data transfer object pattern

2019-01-22 14:23发布

问题:

i'm sorry i'm newbie to enterprise application as well as the design pattern. might be this question occcur lack of knowledge about design pattern. i found that its better to use DTO to transfer data.

my business entity class as below:

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

so in my application user only give ID and HospitalID. so it calls for another web service and get person information

 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; }
 }

so based on these information im going to Patient object. (Using DTO pattern)

so i thought of write new class to convert this as follows.

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;
    }
}

but lately i read few articles and they used Serializer Helper class as well as the XmlSerializer i can't understand why they used somthing like that.

for the DTO pattern is that need to use XmlSerializer and why it is used?

回答1:

You should really take a look at AutoMapper.

http://automapper.org

This is a piece of software that you can include in your solution that will automatically map values from one class to another.

It'll map properties with the same name automatically, and is also pretty smart when it comes to child objects. However, it also offers complete mapping control when you need it.

EDIT

Couple of examples to show how AutoMapper works. Please note I'd never code like this in real life. Brevity!

Example classes.

// 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; }
   }
}

Typically, you'd have a part of your project to configure all your AutoMapping. With the examples I've just given, you can configure a map between Entities.Manager and Models.Manager like so:-

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

Then, in your code, you'd use something like this to get a new Models.Manager object from the Entity version.

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

Incidentally, AM is smart enough to resolve a lot of properties automatically if you name things consistently.

Example above, UserFirstname and UserLastname should be automatically populated because:-

  • Manager has a property called User
  • User has properties called Firstname and Lastname

However, the UserMiddlename property in Models.Manager will always be blank after a mapping op between Entities.Manager and Models.Manager, because User does not have a public property called Middlename.



回答2:

There is a nice, yet simple demo in CodeProject. It is worthy going through it. Newbies can get a basic idea of designing DTOs.

http://www.codeproject.com/Articles/8824/C-Data-Transfer-Object

Here's a summary of the content:

The Data Transfer Object "DTO", is a simple serializable object used to transfer data across multiple layers of an application. The fields contained in the DTO are usually primitive types such as strings, boolean, etc. Other DTOs may be contained or aggregated in the DTO. For example, you may have a collection of BookDTOs contained in a LibraryDTO. I have created a framework used by multiple applications that utilizes DTOs to transfer data across tiers. The framework also relies on other OO patterns such as the Factory, Facade, etc. One of the great things about the DTO compared to a DataSet is that the DTO does not have to directly match a data table or view. The DTO can aggregate fields from another DTO

This is the base class for all Data Transfer Objects.

using System;

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

This is a derived class from 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;
        }
    }

}

This is the helper class for a DTO. It has public methods to serialize and de-serialize a 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;
        }            
    }

}

Now begin Serialization / Deserialization:

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;
    }
}

Finally this code is executed in the main application

static void Main(string[] args)
{
    DemoClass dc = new DemoClass();
    dc.StartDemo();
}


回答3:

An XmlSerializer or JsonSerializer can be used for serializing (loading) XML or Json data from a source (webservice). Or explaining the name DTO: you serialize (transfer) data from a source (webservice) to a (general DTO) object. So DTOs are general purpose objects. Sometimes its clever to make a wide as possible DTO object and fill that completely so you can use from that object whatever you like and copy that to your "own" program objects.

Example: I developped a program for showing transport navigation data. I serialize the whole xml or json messsage in a DTO object. In this DTO object is more information then I will need in my program and it can be in a different form, so I will use only whats needed. DTO objects makes it more easy to extract data from sources (webservices).

I dont want to use AutoMapper because of the name "Auto". I want to know what I am doing and think where my data is going to.