当前位置:网站首页>Introduction of ABP framework (2)

Introduction of ABP framework (2)

2020-12-07 15:04:44 Strategic board

Write in front of you 《ABP frame (1) General introduction of the framework 》 About this ABP The main features of the framework , And introduced me to this framework Web API Some ideas of application priority , This article continues to explore ABP Preliminary use of the framework , That's what we downloaded ABP Framework projects ( be based on ABP Expansion projects of basic projects ), If you understand each component module , And how to use it .

1)ABP Introduction of framework application project

The whole foundation of ABP The framework seems very large , In fact, many projects have little content , It is mainly used to encapsulate different components independently , Such as Automaper、SignalR、MongoDB、Quartz... And so on , Basically, our main concern is Abp In this major project , The other is encapsulation for different component applications .

And based on the foundation ABP Framework extended ABP Application project , It's a lot simpler , We also need to use different components , Only the corresponding basic modules are considered for use , Generally speaking , Mainly based on the warehouse management to achieve the application of database , Therefore, we mainly understand the content of Microsoft's Entity Framework .

This project is an addition to including basic people 、 role 、 jurisdiction 、 authentication 、 Configuration information outside the basic project , And if you start here , Knowledge of some of these inheritance relationships , It will add a lot of difficulties , Because they are based on users 、 The relationship between roles and other objects is very complicated .

I suggest starting with a simple project , That is, a project based on one or two specific application tables , So you can refer to the case project :eventcloud  perhaps  sample-blog-module  project , We may understand it more clearly in the beginning . Here I use eventcloud Project to analyze the relationship between the classes of each layer in the project .

Let's start with a diagram to understand the relationship between the classes in the domain driver module under the framework .

Start with the domain layer , That is, in the project EventCloud.Core The contents are analyzed .

 

2) Code analysis of domain object layer

First , We need classes that understand the relationship between domain objects and databases , That is, domain entity information , This class is very critical , It builds the relationship between the warehousing pattern and the database tables .

[Table("AppEvents")]
    public class Event : FullAuditedEntity<Guid>, IMustHaveTenant
    {
        public virtual int TenantId { get; set; }

        [Required]
        [StringLength(MaxTitleLength)]
        public virtual string Title { get; protected set; }

        [StringLength(MaxDescriptionLength)]
        public virtual string Description { get; protected set; }

        public virtual DateTime Date { get; protected set; }

        public virtual bool IsCancelled { get; protected set; }
     
        ......
   }

This defines the relationship between domain entities and table names , Other properties are the fields corresponding to the database

[Table("AppEvents")]

And then in EventCloud.EntityFrameworkCore Inside the project , Who added to the table DbSet object , As shown in the following code .

namespace EventCloud.EntityFrameworkCore
{
    public class EventCloudDbContext : AbpZeroDbContext<Tenant, Role, User, EventCloudDbContext>
    {
        public virtual DbSet<Event> Events { get; set; }

        public virtual DbSet<EventRegistration> EventRegistrations { get; set; }

        public EventCloudDbContext(DbContextOptions<EventCloudDbContext> options)
            : base(options)
        {
        }
    }
}
 

Simple words , Storage mode can run , We make use of  IRepository<Event, Guid> Interface can obtain many processing interfaces of corresponding tables , Include add delete change check 、 Paging and so on , But in order to isolate the business logic , We introduced Application Service application layer , It also introduces DTO( Data transmission object ) The concept of , To hide our domain object information from the application layer , Achieve more flexible processing . Generally corresponding to domain objects DTO The object definition is as follows .

[AutoMapFrom(typeof(Event))]
    public class EventListDto : FullAuditedEntityDto<Guid>
    {
        public string Title { get; set; }

        public string Description { get; set; }

        public DateTime Date { get; set; }

        public bool IsCancelled { get; set; }

        public virtual int MaxRegistrationCount { get; protected set; }

        public int RegistrationsCount { get; set; }
    }
 

We need to pay attention to the inheritance of entity classes from FullAuditedEntityDto<Guid>, It marks the creation of this domain object 、 modify 、 Deleted tags 、 Time and personnel information , If you need to know more about this part , You can refer to it ABP The introduction of domain entity objects on the official website (Entities).

By adding markedness to the class , We can Event Domain object to EventListDto The object implements automatic mapping . This definition deals with , Generally speaking, there is no problem , But if we need to put DTO( Such as EventListDto) Isolation and domain objects ( Such as Event) The relationship between , hold DTO Separate extraction for public use , Then we can define a domain object mapping file in the application service layer to replace the declarative mapping relationship ,AutoMaper The mapping file for is defined as follows .

public class EventMapProfile : Profile
    {
        public EventMapProfile()
        {
            CreateMap<EventListDto, Event>();
            CreateMap<EventDetailOutput, Event>();
            CreateMap<EventRegistrationDto, EventRegistration>();
        }
    }

This extracts the independent mapping file , We can extract... For us alone DTO Object and application layer interface as a separate project to provide convenience , Because you don't need to rely on domain entities . As I did in the renovation project DTO Layer examples are as follows .

Just now, I introduced domain entities and DTO Object mapping , It is to provide data bearing for application service layer .

If the logical processing of domain objects is more complex , You can also define a similar business logic class ( Like we said BLL), commonly ABP Inside the frame with Manager And that's the concept at the end , For example, in the case , Business logic interfaces and logic classes are defined as follows , Note here that the interface inherits from IDomainService Interface .

 
/// <summary>
    /// Event Business logic class 
    /// </summary>
    public interface IEventManager: IDomainService
    {
        Task<Event> GetAsync(Guid id);
        Task CreateAsync(Event @event);
        void Cancel(Event @event);
        Task<EventRegistration> RegisterAsync(Event @event, User user);
        Task CancelRegistrationAsync(Event @event, User user);
        Task<IReadOnlyList<User>> GetRegisteredUsersAsync(Event @event);
    }
 

The implementation of the business logic class is as follows .

We see the constructor of this class , Several interface object parameters are brought in , This is DI, The concept of dependency injection , These pass through IOC It's easy to inject constructors , We just need to know , After the module starts , These interfaces can be used , If you need to know more about , You can refer to ABP The content introduction of dependency injection on the official website (Dependency Injection).

So we correspond to Application Service Inside , about Event Class of application service layer EventAppService , As shown below .

    [AbpAuthorize]
    public class EventAppService : EventCloudAppServiceBase, IEventAppService
    {
        private readonly IEventManager _eventManager;
        private readonly IRepository<Event, Guid> _eventRepository;

        public EventAppService(
            IEventManager eventManager,
            IRepository<Event, Guid> eventRepository)
        {
            _eventManager = eventManager;
            _eventRepository = eventRepository;
        }

        ......
 

The service layer class here provides two interface Injection , One is the custom event business object class , One is the standard warehousing object .

Most of the time if it's based on Web API Under the architecture of , If it is based on database table processing , I don't think it's necessary to do business management in the field , Use the standard object of warehousing directly , It's enough for most needs , Some logic we can use in Application Service It can be realized as follows .

 

3) Dictionary module business class simplification

We use the dictionary type table of the dictionary module to introduce .

The domain business object interface layer is defined as follows ( similar IBLL)

/// <summary>
    ///  Domain business management interface 
    /// </summary>
    public interface IDictTypeManager : IDomainService
    {
        /// <summary>
        ///  Get a list set of all dictionary types (Key As the name ,Value by ID value )
        /// </summary>
        /// <param name="dictTypeId"> Dictionary type ID, If it is blank, all </param>
        /// <returns></returns>
        Task<Dictionary<string, string>> GetAllType(string dictTypeId);

    }
 

Domain business object management class ( similar BLL)

/// <summary>
    ///  Domain business management class implementation 
    /// </summary>
    public class DictTypeManager : DomainService, IDictTypeManager
    {
        private readonly IRepository<DictType, string> _dictTypeRepository;

        public DictTypeManager(IRepository<DictType, string> dictTypeRepository)
        {
            this._dictTypeRepository = dictTypeRepository;
        }

        /// <summary>
        ///  Get a list set of all dictionary types (Key As the name ,Value by ID value )
        /// </summary>
        /// <param name="dictTypeId"> Dictionary type ID, If it is blank, all </param>
        /// <returns></returns>
        public async Task<Dictionary<string, string>> GetAllType(string dictTypeId)
        {
            IList<DictType> list = null;
            if (!string.IsNullOrWhiteSpace(dictTypeId))
            {
                list = await _dictTypeRepository.GetAllListAsync(p => p.PID == dictTypeId);
            }
            else
            {
                list = await _dictTypeRepository.GetAllListAsync();
            }

            Dictionary<string, string> dict = new Dictionary<string, string>();
            foreach (var info in list)
            {
                if (!dict.ContainsKey(info.Name))
                {
                    dict.Add(info.Name, info.Id);
                }
            }
            return dict;
        }
    }

Then the application service layer interface of the domain object is implemented as follows

    [AbpAuthorize]
    public class DictTypeAppService : MyAsyncServiceBase<DictType, DictTypeDto, string, PagedResultRequestDto, CreateDictTypeDto, DictTypeDto>, IDictTypeAppService
    {
        private readonly IDictTypeManager _manager;
        private readonly IRepository<DictType, string> _repository;

        public DictTypeAppService(
            IRepository<DictType, string> repository, 
            IDictTypeManager manager) : base(repository)
        {
            _repository = repository;
            _manager = manager;
        }

        /// <summary>
        ///  Get a list set of all dictionary types (Key As the name ,Value by ID value )
        /// </summary>
        /// <returns></returns>
        public async Task<Dictionary<string, string>> GetAllType(string dictTypeId)
        {
            var result = await _manager.GetAllType(dictTypeId);
            return result;
        }
......

This is in the application service layer , It integrates the processing of business logic class , But in this way , For normal database processing , It's a bit cumbersome , You need to define one more business object interface and one more business object implementation , At the same time, in the application layer interface , You also need to add an additional interface parameter , The overall feeling is a little redundant , So I changed it to use a standard warehouse object to handle the same thing .

Corresponding position in the project , Delete a business object interface and a business object implementation of dictionary type , Change to interface processing of standard storage object , It's equivalent to putting the code in the business logic on the service layer , The processing code in the application service layer is as follows .

    [AbpAuthorize]
    public class DictTypeAppService : MyAsyncServiceBase<DictType, DictTypeDto, string, PagedResultRequestDto, CreateDictTypeDto, DictTypeDto>, IDictTypeAppService
    {
        private readonly IRepository<DictType, string> _repository;

        public DictTypeAppService(
            IRepository<DictType, string> repository) : base(repository)
        {
            _repository = repository;
        }

        /// <summary>
        ///  Get a list set of all dictionary types (Key As the name ,Value by ID value )
        /// </summary>
        /// <returns></returns>
        public async Task<Dictionary<string, string>> GetAllType(string dictTypeId)
        {
            IList<DictType> list = null;
            if (!string.IsNullOrWhiteSpace(dictTypeId))
            {
                list = await Repository.GetAllListAsync(p => p.PID == dictTypeId);
            }
            else
            {
                list = await Repository.GetAllListAsync();
            }

            Dictionary<string, string> dict = new Dictionary<string, string>();
            foreach (var info in list)
            {
                if (!dict.ContainsKey(info.Name))
                {
                    dict.Add(info.Name, info.Id);
                }
            }
            return dict;
        }

......

So let's define two documents less , And reduce the code for coordinating business classes , The code is simpler and easier to understand , Anyway, the final implementation is based on the interface call of the storage object .

in addition , We continue to understand the project , Know in Web.Host The project is us Web API Layer start , And dynamically build Web API Service layer of layer . It integrated. Swagger Test and use the interface .

// Swagger - Enable this line and the related lines in Configure method to enable swagger UI
            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new Info { Title = "MyProject API", Version = "v1" });
                options.DocInclusionPredicate((docName, description) => true);

                // Define the BearerAuth scheme that's in use
                options.AddSecurityDefinition("bearerAuth", new ApiKeyScheme()
                {
                    Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
                    Name = "Authorization",
                    In = "header",
                    Type = "apiKey"
                });
                // Assign scope requirements to operations based on AuthorizeAttribute
                options.OperationFilter<SecurityRequirementsOperationFilter>();
            });
 

Start project , We can see Swagger The management interface of is as follows .

 

版权声明
本文为[Strategic board]所创,转载请带上原文链接,感谢
https://chowdera.com/2020/12/20201207150436864v.html