Skip to main content
.NET

How to use DTOs the right way in .NET

β€” Christian Schou

Something a lot of developers use every day, but they often use it incorrectly. DTOs are short for Data Transfer Objects. 😎

Have you ever thought about the word DTO? The word itself "DTO" is a general term or category that refers to a design pattern specifically designed to transfer data.

The problem is a lot of developers implement DTOs directly in layers instead of mapping the data and using them for transferring data instead. If you are in doubt about what else should be used, please check out my post below.

What is the difference between POCO, DTO, Entity, and VO?
It’s a common question. What is the difference between a POCO, DTO, Entity, and a VO? One thing they have in common is that they are classes.

Alright - This article will not discuss what is good and bad practice about implementing DTOs, it's about how we name them in the best possible way. Let's get started doing just that! πŸš€

Misusing DTOs In .NET

I am primarily working with .NET Core using .NET 5,6,7, and 8, hence I will be demonstrating some code examples in this short post in that framework.

Here is an example of how you should not name your DTOs in .NET.

public class CarDTO
{
    public int CarId { get; set; }
    public string CarDescription { get; set; }
}

See the problem? πŸ€” It seems OK to me for a standard object? But what is it used for? No clue πŸ˜…

If it was a response model to some sort of query in my database, everything seems fine here, as we only want to return a specific set of properties to the requesting client.

What if I told you that I would like to use this model to create a new car in my application? Imagine an API endpoint where you are allowed to specify the ID of the entity in the application database 🀣 Sure most hackers would love that... I would start to cry if I saw that in the code.

When I make APIs I often make two DTOs and a model/entity.

  • One DTO for sending a request.
  • One DTO for returning a response.
  • One Model/Entity for my application/data layer.

Let's see how we can add that, to avoid problems with users telling us what the ID should be.

public class CarCreateRequestDTO
{
    public string CarDescription { get; set; }
}

public class CarModel
{
    public int CarId { get; set; }
    public string CarDescription { get; set; }
}

public class CarCreateResponseDTO
{
    public int CarId { get; set; }
    public string CarDescription { get; set; }
}

What did I change in my code above? πŸ€” We now have a model for working with data in our application layer and two separate DTOS for handling requests, and responses.

A Huge Benefit Of Using DTOs

This has nothing to do with misusing DTOs, but one thing that I really like about them is that we can use them for the validation of data. Using a package like FluentValidation, we can make lots of rules before we start using it in our application.

How to use FluentValidation in ASP.NET Core Apps (.Net 6)
Learn how to implement FluentValidation in your ASP.NET Core (.NET 6) applications to build strongly-typed validation rules, the easy way.

Here is an example of how easily you can make validation rules using FluentValidation in .NET.

public class CreateGhostSiteListCommandValidator : AbstractValidator<CreateGhostSiteListCommand>
{
    private readonly IApplicationDbContext _context;

    public CreateGhostSiteListCommandValidator(IApplicationDbContext context)
    {
        _context = context;

        RuleFor(x => x.Title)
            .NotEmpty()
            .MaximumLength(200)
            .MustAsync(BeAUniqueTitle)
                .WithMessage("'{PropertyName}' must be unique.")
                .WithErrorCode("Unique");
    }

    public async Task<bool> BeAUniqueTitle(string title, CancellationToken cancellationToken)
    {
		return await _context.SiteLists
			.AllAsync(x => x.Title != title, cancellationToken);
	}
}

The code above is an example from a real-life application I am working on at the moment, but see how easily we can apply rules and validate data. If you want to check it out, you are more than welcome at the link below.

GitHub - Ghostlyzer/GhostLyzer: A nice open-source solution to help users of the Ghost CMS succeed even more by enabling their data. Built on top of .NET and Angular.
A nice open-source solution to help users of the Ghost CMS succeed even more by enabling their data. Built on top of .NET and Angular. - GitHub - Ghostlyzer/GhostLyzer: A nice open-source solution…

Don't Use DTOs When Working With Files πŸ—ƒοΈ

A thing I also often see is developers using DTOs for working with files. Many often mistake the idea of transferring data, but they are not meant to be used for transferring data in the application when working with files.

There is no need to include DTO in the naming because you are not working with a DTO. It's not a request and response object. Use model if you have to include what it is, instead when naming something concerning files.

Here is an example for loading some settings regarding a cache.

/// Wrong - avoid DTO
public class CacheSettingsDTO
{
    public int SlidingExpirationTime { get; set; }
    public int AbsoluteExpirationTime { get; set; }
    public int MaxSize { get; set; }
}

/// Correct
public class CacheSettings
{
    public int SlidingExpirationTime { get; set; }
    public int AbsoluteExpirationTime { get; set; }
    public int MaxSize { get; set; }
}

We are not responding to a request, hence we don't use the word DTO. πŸ˜…

If you want to learn how to load settings from JSON files using IConfiguration in .NET, then check out my article below.

How to retrieve values from IConfiguration in .NET Core
Learn how to retrieve values from IConfiguration in .NET Core and get my top 8 best practices for validation, error handling, and testing.

If you are looking for a little bit more awesome and dynamic way, then check out the options pattern in .NET. You learn how to implement that pattern in your C# application in my post below.

How to use IOptions Pattern to bind configurations in .NET
Learn how to bind configurations to strongly typed classes in .NET with the IOptions pattern in a nice and easy way.

Summary πŸŽ‰

In this short post about how you should not use DTOs you have seen a few examples of code, gotten some reasons for why the examples are bad usage and how to fix it.

All models for data transfer in an application can in theory use the DTO pattern, but often you will not see the naming DTO as a part of the class name.

One thumb rule I live by is that I name all my models in a concise, precise, and meaningful name providing the developer an easy way to understand what the model/entity/poco... etc. is used for.

If you have any questions, please don't hesitate to reach out in the comments below. Until next time, happy coding! ✌️

Christian Schou