Click here to go to the ABP series articles General Catalogue
DDD-based Modern ASP.--ABP Series 16, ABP Application Layer-Data Transfer object (DTOs)
The ABP is "ASP. Boilerplate Project (ASP. NET Template project) "for short.
ABP's official website :http://www.aspnetboilerplate.com
ABP's Open source project on GitHub : https://github.com/aspnetboilerplate
Data transfer objects (data Transfer Objects) are used for data transfer in the application and presentation layers.
The presentation layer incoming data Transfer object (DTO) invokes an application service method, and then the application service executes some specific business logic through the domain object and returns the DTO to the presentation layer. So the presentation layer and the domain layer are completely separated. In applications with good layering, the presentation layer does not directly use domain objects (warehouses, entities).
The role of data transfer objects
Creating a DTO for each app service method looks like a tedious and time-consuming task. But if you use them correctly, this will save your project. Why is it?
(1) Abstract domain layers (abstraction of domain layer)
The data Transfer object in the presentation layer is an efficient abstraction of the domain object. This way your layer (layers) will be properly separated. Even when you want to completely replace the presentation layer, you can continue to use the existing application and domain layers. Instead, you can rewrite the domain layer, modify the database structure, the entity, and the ORM framework, but do not need to make any changes to the presentation layer as long as your application layer does not change.
(2) data hiding (hiding)
Imagine that you have a user entity that has attribute IDs, Name, EmailAddress, and password. If the return value type of the Userappservice getallusers () method is list. This way anyone can see everyone's password, even if you don't print it on the screen. This is not just a security issue, it's also about data hiding. App service should only return the presentation layer needed, not much more just good.
(3) Serialization & Lazy Loading (serialization & lazy load problems)
When you return data (objects) to the presentation layer, the data is likely to be serialized. For example, in an MVC action that returns JSON, your object needs to be serialized into JSON and sent to the client. Returning entities directly to the presentation layer is likely to cause trouble.
In real projects, entities refer to other entities. The user entity references a role entity. So, when you serialize user, role will be serialized as well. and role has a list and permission also quoted Permissiongroup and so on .... Can you imagine that these objects will be serialized? This has the potential to cause the entire database data to be unexpectedly serialized. So how do we fix it? Mark a property as not serializable? No, because you don't know when the property should be serialized when it should not be serialized. So in this case, return a safe serialization, specially customized data transfer object is a good choice oh.
Almost all ORM frameworks support lazy loading. It will only be loaded if you need to load the entity. For example, the user type refers to a role type. When you get the user from the database, the role attribute is not populated. Role is loaded from the database the first time you read the role attribute. So, when you return such an entity to the presentation layer, it is easy to cause side effects (loading from the database). If the serialization tool reads the entity, it will read all the properties recursively, so that your entire database will be read.
There are more problems with using entities in the presentation layer. The best scenario is that the presentation layer should not refer to any assembly that contains the domain layer.
DTO Conventions & Validation
The ABP provides strong support for data transfer objects. It provides a number of related (conventional) types & interfaces and provides recommendations for DTO naming and usage conventions. When you use DTO,ABP like here, it will automate some tasks to make it easier for you.
An example (Example)
Let's look at a complete example. We're going to write an app service method that searches for people based on name and returns the People list. The person entity code is as follows:
Public class person:entity{ publicvirtualstringgetset;} Public Virtual string Get Set ; } Public Virtual string Get Set ; }}
First, we define an application service interface:
Public Interface ipersonappservice:iapplicationservice{ searchpeopleoutput searchpeople (Searchpeopleinput input);}
The ABP recommends naming the Input/ouput object similar to Methodnameinput/methodnameoutput, which requires that input and output be defined separately for each application service method. Even if your method only receives or returns a value, it is also a good idea to create the appropriate DTO type. In this way, your code will be more extensible, and you can add more properties without changing the signature of the method, which does not break existing client applications.
Of course, the method return value may be void, after which you add a return value that does not break the existing app. If your method does not require any parameters, then you do not need to define an input Dto. However, creating an input dto may be a better solution because it may require a parameter in the future. Of course whether to create it depends on you. The input and output DTO types are defined as follows:
Public classsearchpeopleinput:iinputdto{[Stringlength ( +, Minimumlength =1)] Public stringSearchedname {Get;Set; }} Public classsearchpeopleoutput:ioutputdto{ Publiclist<persondto> people {Get;Set; }} Public classpersondto:entitydto{ Public stringName {Get;Set; } Public stringEmailAddress {Get;Set; }}
Validation: As a convention, the Input DTO implements the Iinputdto interface, and the Output DTO implements the Ioutputdto interface. When you declare the iinputdto parameter, the ABP will validate it automatically before the method executes. This is similar to the ASP. NET MVC validation mechanism, but note that the app service is not a controller. The ABP intercepts it and checks for input. See the DTO validation (dto Validation) documentation for more information. Entitydto is a simple type that has the same ID attribute as the entity. If your Entity ID is not of type int you can use it generic version. The Entitydto also implements the Idto interface. You can see that persondto does not contain the password attribute because the presentation layer does not need it.
Before we go any further we achieve ipersonappservice:
Public classpersonappservice:ipersonappservice{Private ReadOnlyipersonrepository _personrepository; PublicPersonappservice (ipersonrepository personrepository) {_personrepository=personrepository; } Publicsearchpeopleoutput searchpeople (searchpeopleinput input) {//Get entity varPeopleentitylist = _personrepository.getalllist (Person = =Person . Name.contains (input. Searchedname)); //Convert to Dto varPeopledtolist =peopleentitylist. Select ( person=Newpersondto {Id=Person . Id, Name=Person . Name, EmailAddress=Person . EmailAddress}). ToList (); return Newsearchpeopleoutput {people =peopledtolist}; }}
We get the entity from the database, convert the entity into a DTO, and return output. Note that we did not manually detect the data validity of input. The ABP will automatically validate it. The ABP even checks if input is null and throws an exception if null. This avoids the fact that we check the data validity manually in each method.
But you probably don't like to manually convert the person entity to persondto. It's really a boring job. This is especially true if the Peson entity contains a large number of attributes.
Automatic mapping between DTOs and entities
Fortunately there are some tools to make mapping (conversion) very simple. AutoMapper is one of them. You can add it to your project through NuGet. Let's use AutoMapper to rewrite the Searchpeople method:
Public searchpeopleoutput searchpeople (searchpeopleinput input) { var peopleentitylist = _ Personrepository.getalllist (person = person) . Name.contains (input. Searchedname)); return New searchpeopleoutput {people = mapper.map<list<persondto>>(peopleentitylist)};}
That's all the code. You can add more properties to entities and DTOs, but the conversion code remains the same. There's only one thing you need to do before you do this: mapping
Mapper.createmap<person, persondto> ();
AutoMapper created the mapped code. In this way, dynamic mapping is not a performance issue. It's fast and convenient. AutoMapper creates a persondto based on the person entity and assigns values to persondto properties according to the naming convention. The naming convention is configurable and flexible. You can also customize the mapping and use more features to view AutoMapper's documentation for more information.
Mapping using attributes (attributes) and extension methods (Mapping using attributes and extension methods)
The ABP provides several attributes and extension methods to define the mappings. Using it you will need to add abp.automapper to your project through NuGet. There are two ways to map using the Automap attribute (attribute), one is to use Automapfrom and Automapto. The other is to use the Mapto extension method. Examples of defining mappings are as follows:
[Automap (typeof/// definition mapping (so there are two ways to map)publicclass myclass1{ Publicstringgetset;} Public class myclass2{ publicstringgetset;}
You can then use the Mapto extension method to map:
varObj1 =NewMyClass1 {Testprop ="Test Value" };varObj2 = Obj1. Mapto<myclass2> ();//A new MyClass2 object is created and will be obj1. The value of Testprop is assigned to the Testprop property of the new MyClass2 object. The code above creates a new MyClass2 object based on MyClass1. You can also map objects that already exist, as follows:varObj1 =NewMyClass1 {Testprop ="Test Value" };varObj2 =NewMyClass2 (); obj1. Mapto (OBJ2); //to set the properties of Obj2 according to Obj1
Secondary interfaces and types
The ABP also provides a number of auxiliary interfaces that define commonly used standardized properties.
Ilimitedresultrequest defines the Maxresultcount property. So you can implement that interface on your input dto to limit the number of result sets.
Ipagedresultrequest extends the ilimitedresultrequest, which adds the Skipcount property. So we implement this interface for paging in Searchpeopleinput:
Public classsearchpeopleinput:iinputdto, ipagedresultrequest{[Stringlength ( +, Minimumlength =1)] Public stringSearchedname {Get;Set; } Public intMaxresultcount {Get;Set; } Public intSkipcount {Get;Set; }}
For paging requests, you can return the output dto that implements Ihastotalcount as the result. Standardized properties help us create reusable code and specifications. Other interfaces and types can be viewed under the Abp.Application.Services.Dto namespace.
I hope that more domestic architects will be able to focus on the ABP project, and perhaps it will help you, perhaps with your participation, this project can develop better.
Welcome to add ABP Architecture Design Exchange QQ Group: 134710707
Click here to go to the ABP series articles General Catalogue
ABP (modern ASP. NET template Development Framework) series 16, ABP Application Layer-Data Transfer object (DTOs)