Analysis of data transmission objects in ABP framework and Application Service _ Practical skills

Source: Internet
Author: User
Tags automap naming convention serialization

Data Transfer Object (DTOs)
Data Transfer Objects (data Transfer Objects) are used for data transmission at the application layer and the presentation layer.

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 a good layering, the presentation layer does not directly use domain objects (warehouses, entities).

1. The role of data transfer objects:
creating a DTO for each application service method appears to be a tedious and time-consuming task. But if you use them correctly, it will save your project. Why, then?

(1) Abstract domain layer (abstraction of domain layer)

The data Transfer object in the presentation layer is an effective abstraction of the domain object. This way your layer (layers) will be properly isolated. Even when you want to completely replace the presentation layer, you can continue to use the existing application layer and domain layer. Conversely, 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 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 GetAllUsers () method for Userappservice is list. So anyone can view the password for everyone, even if you don't print it on the screen. This is not just a security issue, it's also related to data hiding. Application services should only return to the presentation layer required, not many 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 as JSON and sent to the client. Returning the entity directly to the presentation layer is likely to cause trouble.

In real projects, entities refer to other entities. The user entity references the role entity. So, when you serialize user, role will also be serialized. And the role also has a list and permission also quotes Permissiongroup and so on ... can you imagine that these objects will all be serialized? This has the potential to cause the entire database data to be accidentally serialized. So how to solve it? Mark a property as not serializable? No, because you don't know when the attribute should be serialized and when it should not be serialized. So in this case, the return of 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 the role type. When you get user from the database, the role attribute is not populated. The 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 can easily cause side effects (loaded from the database). If the serialization tool reads the entity, it will read all the attributes 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 a domain layer.

2.DTO Engagement & Validation
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. Using DTO,ABP as you do here will automate some tasks to make you more relaxed.

An example (Example)

Let's look at a complete example. We're going to write an application 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
{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);
}

ABP recommends that named Input/ouput objects resemble Methodnameinput/methodnameoutput, and each application service method needs to be defined separately for input and output. Even if your method only receives or returns a value, it is best to create the appropriate DTO type. In this way, your code will be more extensible, you can add more properties without changing the signature of the method, which does not destroy the existing client application.

Of course, the method return value may be void, and then you add a return value that does not break the existing application. If your method does not require any parameters, then you do not need to define an input Dto. But creating an input dto might be a better solution, because the method might 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 class Searchpeopleinput:iinputdto
{
  [stringlength (+ 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;}
}

Validation: As a convention, Input DTO implements Iinputdto interface, Output DTO implements Ioutputdto interface. When you declare the iinputdto parameter, ABP will automatically validate the method before it is executed. This is similar to the asp.net MVC validation mechanism, but note that the application service is not a controller (Controller). ABP it to intercept and check the input. View the DTO validation (DTO Validation) document for more information. Entitydto is a simple type that has the same ID attribute as an entity. If your Entity ID is not an int, 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 presentation layer does not need it.

And before we go further we realize Ipersonappservice:

public class Personappservice:ipersonappservice
{
  private readonly ipersonrepository _personrepository;

  Public Personappservice (ipersonrepository personrepository)
  {
    _personrepository = personrepository;
  }
  Public searchpeopleoutput searchpeople (searchpeopleinput input)
  {
    //Get entity
    var peopleentitylist = _ Personrepository.getalllist (person => person. Name.contains (input. Searchedname));

    Convert to Dto
    var peopledtolist = peopleentitylist
      . Select (person => new persondto
                {
                  Id = person. Id,
                  Name = person. Name,
                  emailaddress = person. EmailAddress
                }). ToList ();

    return new Searchpeopleoutput {people = peopledtolist};
  }



We retrieve the entity from the database, convert the entity to a DTO, and return output. Note that we do not manually detect input data validation. ABP will automatically validate it. ABP even checks whether the input is null and throws an exception if it is null. This avoids the fact that we check the validity of the data manually in each method.

But you probably don't like to manually convert a person entity into a persondto. It's really a boring job. This is especially true when the Peson entity contains a large number of properties.

Automatic mapping between 3.DTO and entities
Fortunately, there are some tools that make mapping (transformation) very simple. AutoMapper is one of them. You can add it to your project by 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 the whole code. You can add more attributes to the entity and DTO, but the conversion code remains unchanged. There's only one thing you need to do before this: map

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 a value to Persondto's properties based on the naming convention. The naming conventions are configurable and flexible. You can also customize the mapping and use more features to view AutoMapper documents for more information.

4. Using attributes (attributes) and extension methods to map (Mapping using Attributes and extension methods)

ABP provides several attributes and extension methods to define mappings. Using it you need to add Abp.automapper to your project via NuGet. There are two ways to map using the Automap feature (attribute), one of which is to use Automapfrom and Automapto. The other is to use the Mapto extension method. Examples of defining mappings are as follows:

 
[Automap (typeof (MyClass2))]//define mappings (so there are two ways to map) public
class MyClass1
{public
  string Testprop {get; set;}
}

public class MyClass2
{public
  string Testprop {get; set;}
}
 

You can then use the Mapto extension method to map:

var obj1 = new MyClass1 {testprop = "Test value"};
var obj2 = obj1. Mapto<myclass2> (); A new MyClass2 object is created and is obj1. The Testprop value 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:
var obj1 = new MyClass1 {testprop = "Test value"};
var obj2 = new MyClass2 ();
Obj1. Mapto (OBJ2); Set OBJ2 properties based on Obj1

5. Auxiliary interfaces and types
ABP also provides a number of secondary interfaces that define common standardized properties.

Ilimitedresultrequest defines the Maxresultcount property. So you can implement the interface on your input dto to limit the number of result sets.

Ipagedresultrequest extends Ilimitedresultrequest, which adds the Skipcount attribute. So we implement this interface in Searchpeopleinput for paging:

public class Searchpeopleinput:iinputdto, ipagedresultrequest
{
  [stringlength (minimumlength = 1)]
  public string Searchedname {get; set;}

  public int Maxresultcount {get; set;}
  public int Skipcount {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. You can view additional interfaces and types under the Abp.Application.Services.Dto namespace.

Application Services
application services are used to expose domain (business) logic to the presentation layer. The presentation layer invokes the application service by passing in the DTO (data transfer object) 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 level project, the presentation layer should never directly access the domain object.

1.IApplicationService interface
in ABP, an application service needs to implement the Iapplicationservice interface. The best practice is to create the appropriate interfaces for each application service. So, we first define an application service interface, as follows:

Public interface Ipersonappservice:iapplicationservice
{
  void Createperson (Createpersoninput input);
}

Ipersonappservice has only one method, and it will be presented with layers to create a new person. Createpersoninput is a Dto object, as follows:


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 are already a person with given e-mail address"); 
    } person

    = new Person {Name = input. Name, EmailAddress = input. EmailAddress};
    _personrepository.insert (person);
  }




Here are a few important tips:

    • Personappservice through IRepository to perform database operations. It is generated through the constructor injection pattern. We are using dependency injection here.
    • Personappservice implements the Iapplicationservice (inherited Iapplicationservice through Ipersonappservice). ABP automatically registers it with the dependency injection system and can be injected into other types.
    • The Createperson method requires an argument of the Createpersoninput type. This is a dto as input, and it will be ABP to automatically validate its data validity. You can view the DTO and data Validation (Validation) documentation for relevant details.

2. Application Service Type

The Application service (application services) needs to implement the Iapplicationservice interface. Of course, you can choose to inherit your application service (application services) from the Applicationservice base class, so that your application services will automatically implement the Iapplicationservice interface. The Applicationservice base class provides convenient logging and localization capabilities. Here we recommend that you create an application service base class for your application to inherit from the Applicationservice type. This allows you to add a few common features that you can use to provide all of your application services. An application service example is shown below:

public class Taskappservice:applicationservice, Itaskappservice
{public
  taskappservice ()
  {
    Localizationsourcename = "Simpletasksystem";
  }

  public void CreateTask (Createtaskinput input)
  {
    //logging, Logger defined in Applicationservice
    logger.info (" Creating a new Task with description: "+ input." Description);

    Get localized text (L is localizationhelper.getstring (...) The simple version, defined in the Applicationservice type)
    var text = L ("Samplelocalizabletextkey");

    Todo:add new task to database ...
  }
}

In this example we defined 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. View the log records (logging) and localization (localization) documents for more relevant information.


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.