ABP开发框架前后端开发系列---(2)框架的初步介绍

1 篇文章 0 订阅

1)ABP框架应用项目的介绍

整个基础的ABP框架看似非常庞大,其实很多项目也很少内容,主要是独立封装不同的组件进行使用,如Automaper、SignalR、MongoDB、Quartz。。。等等内容,基本上我们主要关注的内容就是Abp这个主要的项目里面,其他的是针对不同的组件应用做的封装。

而基于基础ABP框架扩展出来的ABP应用项目,则简单很多,我们也是在需要用到不同组件的时候,才考虑引入对应的基础模块进行使用,一般来说,主要还是基于仓储管理实现基于数据库的应用,因此我们主要对微软的实体框架的相关内容了解清楚即可。

这个项目是一个除了包含基础的人员、角色、权限、认证、配置信息的基础项目外,而如果你从这里开始,对于其中的一些继承关系的了解,会增加很多困难,因为它们基础的用户、角色等对象关系实在是很复杂。

我建议从一个简单的项目开始,也就是基于一两个特定的应用表开始的项目,因此可以参考案例项目:eventcloud 或者 sample-blog-module 项目,我们入门理解起来可能更加清楚。这里我以eventcloud项目来进行分析项目中各个层的类之间的关系。

我们先从一个关系图来了解下框架下的领域驱动模块中的各个类之间的关系。

先以领域层,也就是项目中的EventCloud.Core里面的内容进行分析。

 

2)领域对象层的代码分析

首先,我们需要了解领域对象和数据库之间的关系的类,也就是领域实体信息,这个类非常关键,它是构建仓储模式和数据库表之间的关系的。


    
    
  1. [ Table("AppEvents")]
  2. public class Event : FullAuditedEntity< Guid>, IMustHaveTenant
  3. {
  4. public virtual int TenantId { get; set; }
  5. [ Required]
  6. [ StringLength(MaxTitleLength)]
  7. public virtual string Title { get; protected set; }
  8. [ StringLength(MaxDescriptionLength)]
  9. public virtual string Description { get; protected set; }
  10. public virtual DateTime Date { get; protected set; }
  11. public virtual bool IsCancelled { get; protected set; }
  12. ......
  13. }
    

这个里面定义了领域实体和表名之间的关系,其他属性也就是对应数据库的字段了

[Table("AppEvents")]
    
    

然后在EventCloud.EntityFrameworkCore项目里面,加入这个表的DbSet对象,如下代码所示。


    
    
  1. namespace EventCloud.EntityFrameworkCore
  2. {
  3. public class EventCloudDbContext : AbpZeroDbContext< Tenant, Role, User, EventCloudDbContext>
  4. {
  5. public virtual DbSet<Event> Events { get; set; }
  6. public virtual DbSet<EventRegistration> EventRegistrations { get; set; }
  7. public EventCloudDbContext(DbContextOptions<EventCloudDbContext> options)
  8. : base(options)
  9. {
  10. }
  11. }
  12. }

简单的话,仓储模式就可以跑起来了,我们利用 IRepository<Event, Guid> 接口就可以获取对应表的很多处理接口,包括增删改查、分页等等接口,不过为了进行业务逻辑的隔离,我们引入了Application Service应用层,同时也引入了DTO(数据传输对象)的概念,以便向应用层隐藏我们的领域对象信息,实现更加弹性化的处理。一般和领域对象对应的DTO对象定义如下所示。


    
    
  1. [ AutoMapFrom(typeof(Event))]
  2. public class EventListDto : FullAuditedEntityDto< Guid>
  3. {
  4. public string Title { get; set; }
  5. public string Description { get; set; }
  6. public DateTime Date { get; set; }
  7. public bool IsCancelled { get; set; }
  8. public virtual int MaxRegistrationCount { get; protected set; }
  9. public int RegistrationsCount { get; set; }
  10. }

其中我们需要注意实体类继承自FullAuditedEntityDto<Guid>,它标记这个领域对象会记录创建、修改、删除的标记、时间和人员信息,如果需要深入了解这个部分,可以参考下ABP官网关于领域实体对象的介绍内容(Entities)。

通过在类增加标记性的特性处理,我们可以从Event领域对象到EventListDto的对象实现了自动化的映射。这样的定义处理,一般来说没有什么问题,但是如果我们需要把DTO(如EventListDto)隔离和领域对象(如Event)的关系,把DTO单独抽取来方便公用,那么我们可以在应用服务层定义一个领域对象的映射文件来替代这种声明式的映射关系,AutoMaper的映射文件定义如下所示。


    
    
  1. public class EventMapProfile : Profile
  2. {
  3. public EventMapProfile()
  4. {
  5. CreateMap<EventListDto, Event>();
  6. CreateMap<EventDetailOutput, Event>();
  7. CreateMap<EventRegistrationDto, EventRegistration>();
  8. }
  9. }

这样抽取独立的映射文件,可以为我们单独抽取DTO对象和应用层接口作为一个独立项目提供方便,因为不需要依赖领域实体。如我改造项目的DTO层实例如下所示。

刚才介绍了领域实体和DTO对象的映射关系,就是为了给应用服务层提供数据的承载。

如果领域对象的逻辑处理比较复杂一些,还可以定义一个类似业务逻辑类(类似我们说说的BLL),一般ABP框架里面以Manager结尾的就是这个概念,如对于案例里面,业务逻辑接口和逻辑类定义如下所示,这里注意接口继承自IDomainService接口。


    
    
  1. /// <summary>
  2. /// Event的业务逻辑类
  3. /// </summary>
  4. public interface IEventManager: IDomainService
  5. {
  6. Task<Event> GetAsync(Guid id);
  7. Task CreateAsync(Event @event);
  8. void Cancel(Event @event);
  9. Task<EventRegistration> RegisterAsync(Event @event, User user);
  10. Task CancelRegistrationAsync(Event @event, User user);
  11. Task<IReadOnlyList<User>> GetRegisteredUsersAsync(Event @event);
  12. }

业务逻辑类的实现如下所示。

我们看到这个类的构造函数里面,带入了几个接口对象的参数,这个就是DI,依赖注入的概念,这些通过IOC容易进行构造函数的注入,我们只需要知道,在模块启动后,这些接口都可以使用就可以了,如果需要了解更深入的,可以参考ABP官网对于依赖注入的内容介绍(Dependency Injection)。

这样我们对应的Application Service里面,对于Event的应用服务层的类EventAppService ,如下所示。


    
    
  1. [ AbpAuthorize]
  2. public class EventAppService : EventCloudAppServiceBase, IEventAppService
  3. {
  4. private readonly IEventManager _eventManager;
  5. private readonly IRepository<Event, Guid> _eventRepository;
  6. public EventAppService(
  7. IEventManager eventManager,
  8. IRepository<Event, Guid> eventRepository)
  9. {
  10. _eventManager = eventManager;
  11. _eventRepository = eventRepository;
  12. }

这里的服务层类提供了两个接口注入,一个是自定义的事件业务对象类,一个是标准的仓储对象。

大多数情况下如果是基于Web API的架构下,如果是基于数据库表的处理,我觉得领域的业务管理类也是不必要的,直接使用仓储的标准对象处理,已经可以满足大多数的需要了,一些逻辑我们可以在Application Service里面实现以下即可。

 

3)字典模块业务类的简化

我们以字典模块的字典类型表来介绍。

领域业务对象接口层定义如下所示(类似IBLL)


    
    
  1. /// <summary>
  2. /// 领域业务管理接口
  3. /// </summary>
  4. public interface IDictTypeManager : IDomainService
  5. {
  6. /// <summary>
  7. /// 获取所有字典类型的列表集合(Key为名称,Value为ID值)
  8. /// </summary>
  9. /// <param name="dictTypeId">字典类型ID,为空则返回所有</param>
  10. /// <returns></returns>
  11. Task<Dictionary< string, string>> GetAllType( string dictTypeId);
  12. }

领域业务对象管理类(类似BLL)


    
    
  1. /// <summary>
  2. /// 领域业务管理类实现
  3. /// </summary>
  4. public class DictTypeManager : DomainService, IDictTypeManager
  5. {
  6. private readonly IRepository<DictType, string> _dictTypeRepository;
  7. public DictTypeManager(IRepository<DictType, string> dictTypeRepository)
  8. {
  9. this._dictTypeRepository = dictTypeRepository;
  10. }
  11. /// <summary>
  12. /// 获取所有字典类型的列表集合(Key为名称,Value为ID值)
  13. /// </summary>
  14. /// <param name="dictTypeId">字典类型ID,为空则返回所有</param>
  15. /// <returns></returns>
  16. public async Task<Dictionary< string, string>> GetAllType( string dictTypeId)
  17. {
  18. IList<DictType> list = null;
  19. if (! string.IsNullOrWhiteSpace(dictTypeId))
  20. {
  21. list = await _dictTypeRepository.GetAllListAsync(p => p.PID == dictTypeId);
  22. }
  23. else
  24. {
  25. list = await _dictTypeRepository.GetAllListAsync();
  26. }
  27. Dictionary< string, string> dict = new Dictionary< string, string>();
  28. foreach ( var info in list)
  29. {
  30. if (!dict.ContainsKey(info.Name))
  31. {
  32. dict.Add(info.Name, info.Id);
  33. }
  34. }
  35. return dict;
  36. }
  37. }

然后领域对象的应用服务层接口实现如下所示


    
    
  1. [ AbpAuthorize]
  2. public class DictTypeAppService : MyAsyncServiceBase< DictType, DictTypeDto, string, PagedResultRequestDto, CreateDictTypeDto, DictTypeDto>, IDictTypeAppService
  3. {
  4. private readonly IDictTypeManager _manager;
  5. private readonly IRepository<DictType, string> _repository;
  6. public DictTypeAppService(
  7. IRepository<DictType, string> repository,
  8. IDictTypeManager manager) : base( repository)
  9. {
  10. _repository = repository;
  11. _manager = manager;
  12. }
  13. /// <summary>
  14. /// 获取所有字典类型的列表集合(Key为名称,Value为ID值)
  15. /// </summary>
  16. /// <returns></returns>
  17. public async Task<Dictionary< string, string>> GetAllType( string dictTypeId)
  18. {
  19. var result = await _manager.GetAllType(dictTypeId);
  20. return result;
  21. }

这样就在应用服务层里面,就整合了业务逻辑类的处理,不过这样的做法,对于常规数据库的处理来说,显得有点累赘,还需要多定义一个业务对象接口和一个业务对象实现,同时在应用层接口里面,也需要多增加一个接口参数,总体感觉有点多余,因此我把它改为使用标准的仓储对象来处理就可以达到同样的目的了。

在项目其中对应位置,删除字典类型的一个业务对象接口和一个业务对象实现,改为标准仓储对象的接口处理,相当于把业务逻辑里面的代码提出来放在服务层而已,那么在应用服务层的处理代码如下所示。


    
    
  1. [ AbpAuthorize]
  2. public class DictTypeAppService : MyAsyncServiceBase< DictType, DictTypeDto, string, PagedResultRequestDto, CreateDictTypeDto, DictTypeDto>, IDictTypeAppService
  3. {
  4. private readonly IRepository<DictType, string> _repository;
  5. public DictTypeAppService(
  6. IRepository<DictType, string> repository) : base( repository)
  7. {
  8. _repository = repository;
  9. }
  10. /// <summary>
  11. /// 获取所有字典类型的列表集合(Key为名称,Value为ID值)
  12. /// </summary>
  13. /// <returns></returns>
  14. public async Task<Dictionary< string, string>> GetAllType( string dictTypeId)
  15. {
  16. IList<DictType> list = null;
  17. if (! string.IsNullOrWhiteSpace(dictTypeId))
  18. {
  19. list = await Repository.GetAllListAsync(p => p.PID == dictTypeId);
  20. }
  21. else
  22. {
  23. list = await Repository.GetAllListAsync();
  24. }
  25. Dictionary< string, string> dict = new Dictionary< string, string>();
  26. foreach ( var info in list)
  27. {
  28. if (!dict.ContainsKey(info.Name))
  29. {
  30. dict.Add(info.Name, info.Id);
  31. }
  32. }
  33. return dict;
  34. }
  35. ......

这样我们少定义两个文件,以及减少协调业务类的代码,代码更加简洁和容易理解,反正最终实现都是基于仓储对象的接口调用。

另外,我们继续了解项目,知道在Web.Host项目是我们Web API层启动,且动态构建Web API层的服务层。它整合了Swagger对接口的测试使用。


    
    
  1. // Swagger - Enable this line and the related lines in Configure method to enable swagger UI
  2. services.AddSwaggerGen(options =>
  3. {
  4. options.SwaggerDoc( "v1", new Info { Title = "MyProject API", Version = "v1" });
  5. options.DocInclusionPredicate((docName, description) => true);
  6. // Define the BearerAuth scheme that's in use
  7. options.AddSecurityDefinition( "bearerAuth", new ApiKeyScheme()
  8. {
  9. Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
  10. Name = "Authorization",
  11. In = "header",
  12. Type = "apiKey"
  13. });
  14. // Assign scope requirements to operations based on AuthorizeAttribute
  15. options.OperationFilter<SecurityRequirementsOperationFilter>();
  16. });

启动项目,我们可以看到Swagger的管理界面如下所示。

</article>

https://blog.csdn.net/qq_39569480/article/details/105226839

ABPFrameWork开发指南 入门文档 目 录 1 ABP总体介绍 1 1.1 入门介绍 1 1.1.1 ABP采用了以下技术 2 1.1.2 ABP框架已实现了以下特性 2 1.1.3 ABP适用的场景 4 1.2 多层架构体系 4 1.2.1 言 4 1.2.2 ABP的体系结构 5 1.2.3 领域层 5 1.2.4 应用层 6 1.2.5 基础设施层 6 1.2.6 WEB与展现层 6 1.2.7 其它 7 1.3 模块系统 7 1.3.1 ABP模块系统简介 7 1.3.2 生命期事件 8 1.3.3 模块依赖 9 1.3.4 自定义的模块方法 10 1.4 启动配置 11 1.4.1 配置ABP 11 1.4.2 配置模块 13 1.4.3 为一个模块创建配置 13 2 ABP公共结构 16 2.1 ABP依赖注入 16 2.1.1 传统方式的问题 16 2.1.2 解决方案 18 2.1.3 依赖注入框架 20 2.1.4 ABP依赖注入的基础结构 21 2.1.5 附件 25 2.2 ABP会话管理 26 2.2.1 简介 26 2.2.2 注入会话 27 2.2.3 使用会话属性 27 2.3 ABP日志管理 28 2.3.1 服务器端 28 2.3.2 客户端 32 2.4 ABP设置管理 32 2.4.1 介绍 32 2.4.2 定义设置 33 2.4.3 设置范围 34 2.4.4 获取设置值 35 2.4.5 更改设置 36 2.4.6 关于缓存 36 3 ABP领域层 37 3.1 ABP领域层—实体 37 3.1.1 实体类 37 3.1.2 接口约定 38 3.1.3 IEntity接口 41 3.2 ABP领域层—仓储 42 3.2.1 IRepository接口 42 3.2.2 仓储的实现 47 3.2.3 管理数据库连接 48 3.2.4 仓储的生命周期 48 3.2.5 仓储的最佳实践 48 3.3 ABP领域层—工作单元 49 3.3.1 通用连接和事务管理方法 49 3.3.2 ABP的连接和事务管理 50 3.3.3 工作单元 53 3.3.4 选项 56 3.3.5 方法 57 3.3.6 事件 57 3.4 ABP领域层—数据过滤器 58 3.4.1 介绍 58 3.4.2 预定义过滤器 58 3.4.3 禁用过滤器 60 3.4.4 启用过滤器 61 3.4.5 设定过滤器参数 62 3.4.6 自定义过滤器 62 3.4.7 其它对象关系映射工具 64 3.5 ABP领域层—领域事件 64 3.5.1 事件总线 64 3.5.2 定义事件 65 3.5.3 触发事件 65 3.5.4 事件处理 66 3.5.5 注册处理器 68 3.5.6 取消注册事件 69 4 ABP应用层 71 4.1 ABP应用层—应用服务 71 4.1.1 IApplicationService接口 71 4.1.2 应用服务类型 73 4.1.3 工作单元 74 4.1.4 应用服务的生命周期 76 4.2 ABP应用层—数据传输对象 76 4.2.1 数据传输对象的作用 76 4.2.2 DTO 约定 & 验证 78 4.2.3 DTO和实体间的自动映射 80 4.2.4 辅助接口和类型 82 4.3 ABP应用层—DTO有效性验证 82 4.3.1 使用数据注解 83 4.3.2 自定义检验 84 4.3.3 设置缺省值 85 4.4 ABP应用层—权限认证 86 4.4.1 定义权限 86 4.4.2 检查权限 87 4.5 ABP应用层—审计日志 90 4.5.1 配置 91 4.5.2 通过属性来启用和禁用审计日志 92 4.5.3 说明 93 5 ABP表现层 94 5.1 ABP展现层—动态WebApi层 94 5.1.1 建立动态web api 控制器 94 5.1.2 使用动态js代理 95 5.2 ABP展现层—本地化 97 5.2.1 程序语言 97 5.2.2 本地化源文件 98 5.2.3 获得一个本地化配置文件 100 5.2.4 总结 103 5.3 ABP展现层—Javascript函数库 103 5.3.1 AJAX 103 5.3.2 通知 107 5.3.3 消息 107 5.3.4 用户界面的繁忙提示 109 5.3.5 Js日志接口 110 5.3.6 Javascript公共方法 111 5.4 ABP展现层—导航栏 112 5.4.1 创建菜单 112 5.4.2 显示菜单 114 5.5 ABP展现层—异常处理 114 5.5.1 开启错误处理 115 5.5.2 非Ajax请求 115 5.5.3 AJAX请求 117 5.5.4 异常事件 118 5.6 ABP展现层—嵌入资源文件 118 6 ABP基础设施层 119 6.1 ABP基础设施层—集成Entity Framework 119 6.1.1 Nuget包 119 6.1.2 创建DbContext 119 6.1.3 仓储 120 6.2 ABP基础设施层—集成NHibernate 124 6.2.1 Nuget包 125 6.2.2 配置 125 6.2.3 仓储实现 127 7 ABP实例一:ASP.NET Boilerplate 131 7.1 引子 131 7.2 什么是ASP.Net Boilerplate? 132 7.3 ABP不适用于那些场合? 133 7.4 开始 134 7.5 使用模板创建空的网站应用程序 134 7.6 领域层 136 7.6.1 实体-Entities 137 7.6.2 仓储-Repository 138 7.6.3 关于命名空间 139 7.7 基础设施层 139 7.7.1 数据库迁移 139 7.7.2 实体映射 143 7.7.3 仓储实现 143 7.8 应用层 145 7.8.1 应用服务及数据传输对象 145 7.8.2 DTO验证 149 7.8.3 动态Web API控制器 150 7.9 表现层 151 7.9.1 单页应用 152 7.9.2 视图和视图模型 152 7.9.3 本地化 160 7.9.4 JavaScript API 162 7.10 更多 164 7.10.1 模块系统 164 7.10.2 依赖注入和约定 165 7.11 结论 165 8 ABP实例二:单页面网站应用程序 167 8.1 简介 167 8.2 基于Abp创建应用程序 168 8.3 创建实体 169 8.4 创建DbContext 170 8.5 创建Database Migrations 171 8.6 定义仓储 173 8.7 实现仓储 174 8.8 构建应用程序服务 175 8.9 验证 179 8.10 异常处理 180 8.11 构建Web API 服务 181 8.12 开发单页面应用(SPA) 181 8.13 本地化 186
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值