C# DTO’s to Models

C# DTO’s to Models

what are DTO’s or Data Transfer Objects. It’s a design pattern used to transfer data between software application subsystems or layers, often between the data access layer and the business logic layer. The purpose of a DTO is to encapsulate data and send it from one part of the application to another, typically across a network or between different parts of a distributed system.

DTOs are used to:

  • Transfer Data: DTOs encapsulate data that needs to be transferred between different parts of an application or between different applications.
  • Decouple Layers: DTOs help in decoupling different layers of an application, such as the presentation layer, business logic layer, and data access layer. They provide a clean separation of concerns by allowing each layer to work with its own representation of data.
  • Customize Data for Presentation: DTOs can be used to shape data specifically for presentation purposes. For example, a DTO might include only the data needed for displaying information in a UI, hiding sensitive or unnecessary details.
  • Optimize Data Transfer: DTOs can reduce the amount of data transferred between subsystems by including only the necessary information, which can improve performance, especially in distributed systems or when working with limited bandwidth.
  • Versioning and Compatibility: DTOs can help in managing versioning and compatibility issues between different parts of an application or between different versions of an application. Changes to the internal data structures can be isolated within the DTOs, preventing breaking changes.

In summary, DTOs provide a way to transfer data between different parts of an application or between different applications while promoting decoupling, performance optimization, and versioning compatibility. They are commonly used in modern software development, especially in distributed systems and service-oriented architectures.

My SQL Tables

I’m going to use a simple example to show how DTO’s work. We have a Family table which has a relation to the Address table and to the Pets table. most people don’t have many addresses but do have many pets. when we write a query well probably get many rows for a single family due to there being many pets. essentially well have duplicated data across many rows. This is because were flattening the data relationships we have across the tables.

CREATE TABLE Family (
    id INT AUTO_INCREMENT PRIMARY KEY,
    familyName VARCHAR(255),
    fk_addressid INT,
    fk_petid INT,
    FOREIGN KEY (fk_addressid) REFERENCES Address(id),
    FOREIGN KEY (fk_petid) REFERENCES Pet(id)
);

CREATE TABLE Address (
    id INT AUTO_INCREMENT PRIMARY KEY,
    number VARCHAR(255),
    road VARCHAR(255),
    city VARCHAR(255),
    state VARCHAR(255),
    zip VARCHAR(10)
);

CREATE TABLE Pet (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255),
    age INT,
    type VARCHAR(255)
);

Query

In the query were joining the three tables together, importantly within the elect statement were aliasing the column names to match the DTO, we could use decorators to dot he same task on the C# side.

SELECT f.id AS FamilyId, f.familyName, 
       a.id AS AddressId, a.number, a.road, a.city, a.state, a.zip,
       p.id AS PetId, p.name AS PetName, p.age AS PetAge, p.type AS PetType
FROM Family f
JOIN Address a ON f.fk_addressid = a.id
JOIN Pet p ON f.fk_petid = p.id
WHERE f.id = @FamilyId;

DTO

Our DTO model represents the returned columns from the query, since it’s returns multiple rows the Data Access layer will probably be written to return an IEnumerable, List, Observable Collection etc.

public class FamilyDTO
{
    public int FamilyId { get; set; }
    public string FamilyName { get; set; }
    public int AddressId { get; set; }
    public string Number { get; set; }
    public string Road { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
    public int PetId { get; set; }
    public string PetName { get; set; }
    public int PetAge { get; set; }
    public string PetType { get; set; }
}

Models

We can see in the Family model we have a List for the pets, and an Address model.

public class Family
{
    public int FamilyId { get; set; }
    public string FamilyName { get; set; }
    public Address Address { get; set; }
    public List<Pet> Pets { get; set; }
}

public class Address
{
    public int AddressId { get; set; }
    public string Number { get; set; }
    public string Road { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zip { get; set; }
}

public class Pet
{
    public int PetId { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Type { get; set; }
}

DTO to Model

Within the DTO class well create a Static Convert method that takes in a List of Family DTO’s. The first thing is to break up the list into groups of family DTO’s and then process each family. Then well take the first row and build an address object, next well create a list of pet models using linq.

Finally;

The Key property is a property of the IGrouping interface, which represents a collection of objects that have a common key. In this case, familyGroup is an instance of IGrouping, where TKey represents the key (in this case, the anonymous type { dto.FamilyId, dto.FamilyName }) and TElement represents the type of elements in the group (in this case, FamilyDTO).

The Key property returns the key of the group, which allows us to access the properties of the key (in this case, FamilyId and FamilyName). So, familyGroup.Key.FamilyId and familyGroup.Key.FamilyName are accessing the properties of the key to retrieve the family ID and family name, respectively.

Now that we have the family created we can add it to the returning list.

public static List<Family> ConvertToFamily(List<FamilyDTO> familyDTOs)
{
    var families = new List<Family>();

    var familyGroups = familyDTOs.GroupBy(dto => new { dto.FamilyId, dto.FamilyName });

    foreach (var familyGroup in familyGroups)
    {
        var addressDTO = familyGroup.First();
        var address = new Address
        {
            AddressId = addressDTO.AddressId,
            Number = addressDTO.Number,
            Road = addressDTO.Road,
            City = addressDTO.City,
            State = addressDTO.State,
            Zip = addressDTO.Zip
        };

        var pets = familyGroup.Select(dto => new Pet
        {
            PetId = dto.PetId,
            Name = dto.PetName,
            Age = dto.PetAge,
            Type = dto.PetType
        }).ToList();

        families.Add(new Family
        {
            FamilyId = familyGroup.Key.FamilyId,
            FamilyName = familyGroup.Key.FamilyName,
            Address = address,
            Pets = pets
        });
    }
    return families;
}

Here are the key points to remember:

Purpose:

DTOs are used to encapsulate and transfer data between software application subsystems or layers, promoting clean separation of concerns and decoupling between layers.

Benefits:

DTOs enable clear communication and data exchange between different parts of an application or between different applications.
They help in optimizing data transfer, reducing bandwidth usage, and improving performance, especially in distributed systems.
DTOs promote maintainability and versioning compatibility by providing a stable data transfer interface between different versions of an application or between different systems.
Common Use Cases:

DTOs are commonly used in service-oriented architectures, web APIs, and microservices to transfer data between client and server.
They are used to shape data specifically for presentation purposes in user interfaces, hiding sensitive or unnecessary details.
DTOs facilitate the integration of external systems or third-party services by providing a standardized data exchange format.
In summary, DTOs are a fundamental tool in software development, enabling efficient and flexible data transfer between different parts of an application or between different systems. Understanding their role and benefits is essential for building robust and maintainable software solutions.