在前面随笔《ABP开发框架前后端开发系列---(1)框架的总体介绍》大概介绍了这个ABP框架的主要特点,以及介绍了我对这框架的Web API应用优先的一些看法,本篇继续探讨ABP框架的初步使用,也就是我们下载到的ABP框架项目(基于ABP基础项目的扩展项目),如果理解各个组件模块,以及如何使用。

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

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

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

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

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

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

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

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

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

    [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; } ......
}

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

[Table("AppEvents")]

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

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)
{
}
}
}

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

    [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; }
}

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

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

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

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

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

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

    /// <summary>
/// Event的业务逻辑类
/// </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);
}

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

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

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

    [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;
} ......

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

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

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

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

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

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

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

    /// <summary>
/// 领域业务管理类实现
/// </summary>
public class DictTypeManager : DomainService, IDictTypeManager
{
private readonly IRepository<DictType, string> _dictTypeRepository; public DictTypeManager(IRepository<DictType, string> dictTypeRepository)
{
this._dictTypeRepository = dictTypeRepository;
} /// <summary>
/// 获取所有字典类型的列表集合(Key为名称,Value为ID值)
/// </summary>
/// <param name="dictTypeId">字典类型ID,为空则返回所有</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;
}
}

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

    [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>
/// 获取所有字典类型的列表集合(Key为名称,Value为ID值)
/// </summary>
/// <returns></returns>
public async Task<Dictionary<string, string>> GetAllType(string dictTypeId)
{
var result = await _manager.GetAllType(dictTypeId);
return result;
}
......

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

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

    [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>
/// 获取所有字典类型的列表集合(Key为名称,Value为ID值)
/// </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;
} ......

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

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

            // 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>();
});

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

ABP开发框架前后端开发系列---(2)框架的初步介绍的更多相关文章

  1. ABP开发框架前后端开发系列---(3)框架的分层和文件组织

    在前面随笔<ABP开发框架前后端开发系列---(2)框架的初步介绍>中,我介绍了ABP应用框架的项目组织情况,以及项目中领域层各个类代码组织,以便基于数据库应用的简化处理.本篇随笔进一步对 ...

  2. ABP开发框架前后端开发系列---(9)ABP框架的权限控制管理

    在前面两篇随笔<ABP开发框架前后端开发系列---(7)系统审计日志和登录日志的管理>和<ABP开发框架前后端开发系列---(8)ABP框架之Winform界面的开发过程>开始 ...

  3. ABP开发框架前后端开发系列---(8)ABP框架之Winform界面的开发过程

    在前面随笔介绍的<ABP开发框架前后端开发系列---(7)系统审计日志和登录日志的管理>里面,介绍了如何改进和完善审计日志和登录日志的应用服务端和Winform客户端,由于篇幅限制,没有进 ...

  4. ABP开发框架前后端开发系列---(16)ABP框架升级最新版本的经验总结

    有一小段时间没有持续升级ABP框架了,最近就因应客户的需要,把ABP框架进行全面的更新,由于我们应用的ABP框架,基础部分还是会使用官方的内容,因此升级的时候需要把官方基础ABP的DLL进行全面的更新 ...

  5. ABP开发框架前后端开发系列---(10)Web API调用类的简化处理

    在较早期的随笔<ABP开发框架前后端开发系列---(5)Web API调用类在Winform项目中的使用>已经介绍了Web API调用类的封装处理,虽然这些调用类我们可以使用代码生成工具快 ...

  6. ABP开发框架前后端开发系列---(5)Web API调用类在Winform项目中的使用

    在前面几篇随笔介绍了我对ABP框架的改造,包括对ABP总体的介绍,以及对各个业务分层的简化,Web API 客户端封装层的设计,使得我们基于ABP框架的整体方案越来越清晰化, 也越来越接近实际的项目开 ...

  7. ABP开发框架前后端开发系列---(4)Web API调用类的封装和使用

    在前面随笔介绍ABP应用框架的项目组织情况,以及项目中领域层各个类代码组织,以及简化了ABP框架的各个层的内容,使得我们项目结构更加清晰.上篇随笔已经介绍了字典模块中应用服务层接口的实现情况,并且通过 ...

  8. ABP开发框架前后端开发系列---(11)菜单的动态管理

    在前面随笔<ABP开发框架前后端开发系列---(9)ABP框架的权限控制管理>中介绍了基于ABP框架服务构建的Winform客户端,客户端通过Web API调用的方式进行获取数据,从而实现 ...

  9. ABP开发框架前后端开发系列---(12)配置模块的管理

    一般来说,一个系统或多或少都会涉及到一些系统参数或者用户信息的配置,而ABP框架也提供了一套配置信息的管理模块,ABP框架的配置信息,必须提前定义好配置的各项内容,然后才能在系统中初始化或者通过接口查 ...

随机推荐

  1. usb driver编写 (转)

    在开头补上LDD3的一句话:如果 USB 驱动没有和另一种处理用户和设备交互的子系统(例如 input, tty, video, 等待)关联, 驱动可使用 USB 主编号为了使用传统的和用户空间之间的 ...

  2. Do not pour out HDU - 5954 数学积分

    题目:题目链接 思路:纯高等数学问题,不过不是很好积分,具体积分思路及过程参考大佬博客——https://blog.csdn.net/danliwoo/article/details/53002695 ...

  3. ACM-ICPC 2018 徐州赛区网络预赛 F. Features Track

    262144K   Morgana is learning computer vision, and he likes cats, too. One day he wants to find the ...

  4. PAT Basic 1080

    1080 MOOC期终成绩 对于在中国大学MOOC(http://www.icourse163.org/ )学习“数据结构”课程的学生,想要获得一张合格证书,必须首先获得不少于200分的在线编程作业分 ...

  5. Linux学习-备份策略

    每部主机的任务都不相同,重要的数据也不相同,重要性也不一样,因此,每个人的备份思考角度都不一样! 备份分为两大部分,一个是每日备份经常性变动的重要数据, 一个则是每周备份就不常变动的信息.这个时候我就 ...

  6. HTTP和HTTPS以及两者的区别

    HTTP:是互联网上的应用广泛的一种网络协议,是一个客户端和服务器端请求和应答的传输协议,它可以使浏览器更加高效,使网络传输减少. HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版, ...

  7. Asp.net HttpWebRequest和HttpWebResponse发送和接受任何类型数据

    发送字符串数据发送数据 string strId = "guest"; "; string postData = "userid=" + strId; ...

  8. 记一次WMS的系统改造(3)— 行进中的复盘

    行进中的波折 革新总会面对一些阻力和风险,一种新的观念.一种新的模式要来替代既有的产品,从来都不是一件简单的事,在WMS改造这件事上我们一开始就提出两种概念货物驱动和任务驱动,并找到一个标杆产品Sla ...

  9. 《Python全栈开发指南》第3版 Alex著(LFXC2018)

    第一章 Python基础——Python介绍&循环语句 1.1 编程语言介绍 1.2 Python介绍 1.3 Python安装 1.4 第一个Python程序 1.5 变量 1.6 程序交互 ...

  10. BBS-登录

    from django.db import models # Create your models here. from django.contrib.auth.models import Abstr ...