Analysis of data transmission objects and application services in the ABP framework

Source: Internet
Author: User
Tags automap

Analysis of data transmission objects and application services in the ABP framework

Data transmission object (DTOs)
Data Transfer Objects is used for Data transmission at the application layer and presentation layer.

The presentation layer transmits the data transmission object (DTO) to call 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. In this way, the presentation layer and the domain layer are completely separated. In applications with good layering, the presentation layer does not directly use domain objects (repositories, entities ).

1. Role of the data transmission object:
Creating DTO for each application service method seems to be a tedious task. But if you use them correctly, this will save your project. Why?

(1) Abstract action of domain layer)

In the presentation layer, data transmission objects effectively abstract domain objects. In this way, your layers will be properly isolated. Even if you want to completely replace the presentation layer, you can continue to use the existing application layer and domain layer. On the contrary, you can rewrite the domain layer, modify the database structure, entity, and ORM framework, but do not need to make any changes to the presentation layer, as long as your application layer has not changed.

(2) Data hiding)

Imagine that you have a User entity with the attributes Id, Name, EmailAddress, and Password. If the return value type of the GetAllUsers () method of UserAppService is List. In this way, anyone can view everyone's password, even if you haven't printed it on the screen. This is not just a security issue, but also related to data hiding. The Application Service should only return what is required by the presentation layer, not many are just right.

(3) Serialization & lazy load problems)

When you return data (objects) to the presentation layer, the data may be serialized. For example, in an MVC Action that returns Json, your object needs to be serialized into JSON and sent to the client. It may be troublesome to directly return an object to the presentation layer.

In real projects, entities reference other entities. The User object references the Role object. Therefore, when you serialize a User, the Role will also be serialized. In addition, Role also has a List and Permission also references PermissionGroup .... Can you imagine that all these objects will be serialized? This may cause the whole database data to be serialized unexpectedly. How can this problem be solved? Mark attributes as non-serializable? No, because you don't know when the attribute should be serialized or not serialized. Therefore, in this case, it is a good choice to return a safe serialization and specially customized data transmission object.

Almost all ORM frameworks support inert loading. It is loaded only when you need to load an object. For example, the User type references the Role type. When you get a User from the database, the Role attribute is not filled. The first time you read the Role attribute, the Role is loaded from the database. Therefore, when you return such an entity to the presentation layer, it can easily cause side effects (loading from the database ). If the serialization tool reads objects, it recursively reads all attributes so that your entire database will be read.

There are more problems when using entities in the presentation layer. The best solution is that the presentation layer should not reference any assembly containing the domain layer.

2. DTO conventions & Verification
ABP provides powerful support for data transmission objects. It provides some related (Conventional) Types and interfaces, and provides suggestions for DTO naming and usage conventions. When you use DTO like this one, you can automate some tasks to make it easier.

Example)

Let's look at a complete example. We want to write an application service method to search for people by name and return the people list. The Person entity code is as follows:

public class Person : Entity{  public virtual string Name { get; set; }  public virtual string EmailAddress { get; set; }  public virtual string Password { get; set; }}

First, we define an application service interface:

public interface IPersonAppService : IApplicationService{  SearchPeopleOutput SearchPeople(SearchPeopleInput input);}

We recommend that you name the input/ouput object similar to MethodNameInput/MethodNameOutput. For each application service method, the Input and Output must be defined separately. Even if your method only receives or returns a value, you 'd better create a corresponding DTO type. In this way, your code will be more extensible. You can add more attributes without changing the method signature, which will not damage the existing client applications.

Of course, the return value of a method may be void. Adding a return value does not destroy the existing application. If your method does not require any parameters, you do not need to define an Input Dto. However, creating an Input Dto may be a better solution, because this method may need 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 class SearchPeopleInput : IInputDto{  [StringLength(40, MinimumLength = 1)]  public string SearchedName { get; set; }}public class SearchPeopleOutput : IOutputDto{  public List<PersonDto> People { get; set; }}public class PersonDto : EntityDto{  public string Name { get; set; }  public string EmailAddress { get; set; }}

Verification: As agreed, Input DTO implements the IInputDto interface, and Output DTO implements the IOutputDto interface. When you declare the IInputDto parameter, the TTL automatically verifies the validity of the method before it is executed. This is similar to ASP. net mvc authentication mechanism, but note that the application service is not a Controller ). It is intercepted and the input is checked. View the DTO Validation document for more information. EntityDto is a simple type with the same Id attribute as the object. If your object Id is not of the int type, you can use its generic version. EntityDto also implements the IDto interface. You can see that PersonDto does not contain the Password attribute, because the display layer does not need it.

Before proceeding, we will first implement IPersonAppService:

Public class PersonAppService: IPersonAppService {private readonly role _ personRepository; public PersonAppService (IPersonRepository personRepository) {_ personRepository = personRepository;} public SearchPeopleOutput SearchPeople (SearchPeopleInput) {// obtain the var peopleEntityList = _ personRepository object. getAllList (person => person. name. contains (input. searchedName); // convert to DTO var export ledtolist = leleentitylist. select (person => new PersonDto {Id = person. id, Name = person. name, EmailAddress = person. emailAddress }). toList (); return new SearchPeopleOutput {People = Your ledtolist };}}

 
We obtain the entity from the database, convert the entity to DTO, and return the output. Note that we did not manually check the validity of Input data. It is automatically verified by the ABP. If the Input value is null, an exception is thrown. This avoids manual check of Data Validity in each method.

However, you may not like to manually convert a Person object to a PersonDto object. This is really a boring job. This is especially true when a Peson object contains a large number of attributes.

3. Automatic ing between DTO and Entity
Fortunately, some tools can make ing (conversion) very simple. AutoMapper is one of them. You can add it to your project through nuget. Let's use AutoMapper to override 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) };}

This is all the code. You can add more attributes in the entity and DTO, but the conversion code remains unchanged. Before that, you only need to do one thing: ing

Mapper.CreateMap<Person, PersonDto>();

AutoMapper creates the ing code. In this way, dynamic ing will not become a performance issue. It's fast and convenient. AutoMapper creates a PersonDto object based on the Person object and assigns a value to the PersonDto Attribute Based on the naming conventions. Naming Conventions are configurable and flexible. You can also customize mappings and use more features. For more information, see the AutoMapper documentation.

4. attriing (Mapping using attributes and extension methods) using attributes and extension methods)

ABP provides several attributes and extension methods to define mappings. To use it, you need to use nuget to add Abp. AutoMapper to your project. Using the AutoMap feature (attribute) can be mapped in either of the following ways: AutoMapFrom and AutoMapTo. The other is to use the MapTo extension method. An example of ing definition is as follows:

[AutoMap (typeof (MyClass2)] // defines ing (there are two ways to map) public class MyClass1 {public string TestProp {get; set ;}} public class MyClass2 {public string TestProp {get; set ;}}

You can use the MapTo Extension Method to map data:

Var obj1 = new MyClass1 {TestProp = "Test value"}; var obj2 = obj1.MapTo <MyClass2> (); // a new MyClass2 object is created, assign the value of obj1.TestProp to the TestProp attribute of the new MyClass2 object. The above Code creates a new MyClass2 object based on MyClass1. You can also map existing objects as follows: var obj1 = new MyClass1 {TestProp = "Test value"}; var obj2 = new MyClass2 (); obj1.MapTo (obj2 ); // set the obj2 Attribute Based on obj1.

5. Auxiliary interfaces and types
ABPS also provides some auxiliary interfaces to define common standardized attributes.

ILimitedResultRequest defines the MaxResultCount attribute. Therefore, you can implement this interface on your Input DTO to limit the number of result sets.

IPagedResultRequest extends ILimitedResultRequest, which adds the SkipCount attribute. Therefore, we implement this interface in SearchPeopleInput for paging:

public class SearchPeopleInput : IInputDto, IPagedResultRequest{  [StringLength(40, MinimumLength = 1)]  public string SearchedName { get; set; }  public int MaxResultCount { get; set; }  public int SkipCount { get; set; }}

For paging requests, you can use Output DTO of IHasTotalCount as the returned result. Standardized attributes help us create reusable code and specifications. You can view other interfaces and types in the name of "ABC. Application. Services. Dto.

Application Service
Application Services are used to expose the domain (business) logic to the presentation layer. The presentation layer calls the application service by passing in the DTO parameter, and the application service executes the corresponding business logic through the domain object and returns the DTO to the presentation layer. Therefore, the presentation layer and the domain layer will be completely isolated. In an ideal hierarchical project, the presentation layer should never directly access domain objects.

1. IApplicationService Interface
An Application Service needs to implement the IApplicationService interface in the ABP. The best practice is to create corresponding interfaces for each application service. Therefore, we first define an application service interface, as shown below:

public interface IPersonAppService : IApplicationService{  void CreatePerson(CreatePersonInput input);}

IPersonAppService has only one method, which is called by the presentation layer to create a new Person. CreatePersonInput is a DTO object, as shown below:

 

public class CreatePersonInput : IInputDto{  [Required]  public string Name { get; set; }  public string EmailAddress { get; set; }}
Next, we implement the IPersonAppService interface:
public class PersonAppService : IPersonAppService{  private readonly IRepository<Person> _personRepository;  public PersonAppService(IRepository<Person> personRepository)  {    _personRepository = personRepository;  }  public void CreatePerson(CreatePersonInput input)  {    var person = _personRepository.FirstOrDefault(p => p.EmailAddress == input.EmailAddress);    if (person != null)    {      throw new UserFriendlyException("There is already a person with given email address");    }    person = new Person { Name = input.Name, EmailAddress = input.EmailAddress };    _personRepository.Insert(person);  }}

 
Important tips:

  • PersonAppService uses IRepository to perform database operations. It is generated by the constructor injection mode. Here we use dependency injection.
  • PersonAppService implements IApplicationService (inherits IApplicationService through IPersonAppService ). It is automatically registered to the dependency injection system and can be injected to other types for use.
  • The CreatePerson method requires a CreatePersonInput parameter. This is a DTO as input, and it will be automatically verified by ABP for its data validity. You can view the DTO and data Validation documents for details.

2. Application Service Type

Application Services must implement the IApplicationService interface. Of course, you can choose to inherit your Application Services from the ApplicationService base class, so that your Application Services will naturally implement the IApplicationService interface. The ApplicationService base class provides convenient logging and localization functions. We recommend that you create an Application Service base class for your application to inherit from the ApplicationService type. In this way, you can add some public functions for all your application services. An Application Service example is as follows:

Public class TaskAppService: ApplicationService, ITaskAppService {public TaskAppService () {LocalizationSourceName = "SimpleTaskSystem";} public void CreateTask (CreateTaskInput input) {// records logs. Logger is defined in ApplicationService Logger. info ("Creating a new task with description:" + input. description); // obtain the localized text (L is LocalizationHelper. getString (...) is defined in the ApplicationService type) var text = L ("SampleLocalizableTextKey"); // TODO: Add new task to database ...}}

In this example, we define LocalizationSourceName In the constructor, but you can define it in the base class so that you do not need to define it in each specific application service. You can view more information about logging and localization.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.