One classification of objects in many applications is the Data Transfer Object, or DTO. Here are some more tips that may help you avoid problems when using these objects.
One classification of objects in many applications is the Data Transfer Object, or DTO. Here are some more tips that may help you avoid problems when using these objects.
Thanks to DevIQ for sponsoring this episode! Check out their list of available courses and how-to videos.
Last week we talked about the definition of a DTO and how they're typically used. This week we'll cover a few more common problems with them and offer some Dos and Don'ts.
It's fairly common to need to map to a DTO and another type, such as an entity. If you're doing this in several places, it's a good idea to consolidate the mapping code in one place. A static factory method on the DTO is a common approach to this. Note that this isn't adding behavior to the DTO, but rather is just a static helper method that we're putting on the DTO type for organizational purposes. I usually name such methods with a From
prefix, such as FromCustomer(Customer customer)
for a CustomerDTO
type. There's a simple example in the show notes for episode 8.
public class CustomerDTO
{
public string FirstName { get; set; }
public string LastName { get; set;}
public static CustomerDTO FromCustomer(Customer customer)
{
return new CustomerDTO()
{
FirstName = customer.FirstName,
LastName = customer.LastName
};
}
}
You can also use a tool like AutoMapper, which will eliminate the need to use such static factory methods. I usually quickly end up moving to AutoMapper if I have more than a couple of these methods to write myself.
It's common in ASP.NET MVC apps to use attributes from the System.ComponentModel.DataAnnotations
namespace to decorate model types for validation purposes. For example, you can add a Required
attribute to a property, and during model binding if that property isn't, an error will be added to a collection of validation errors. Since these attributes don't impact your ability to work with the class as a DTO, and since typically the DTO is tailor made for the purpose of doing this binding, I think it's perfectly reasonable to use these attributes for this purpose. You can rethink this decision if at some point the attributes start to cause you pain. Follow Pain Driven Development (PDD): if something hurts, take a moment to analyze and correct the problem. Otherwise, keep on delivering value to your customers.
If you're not a fan of attribute-based validation, you can use Fluent Validation and define your validation logic using a fluent interface. You'll find a link in the show notes.
Avoid referencing non-DTO or primitive types from your DTOs. This can pull in dependencies that can make it difficult to secure your DTO. In some cases, it can introduce security vulnerabilities, such as if you have methods accepting input as DTOs, and these DTOs reference entities that your app is directly updating in the database. An attacker could guess at the structure of the entity and perhaps its navigation properties and could add or update data outside of the bounds of what you thought you were accepting. Take care in your update operations to only update specific fields, rather than model binding an entity object from external input and then saving it.
Let's wrap up with some quick dos and don't for Data Transfer Objects: