介绍
我们基于不同的需求创建不同的应用,但却在一次又一次地实现相同或相似的结构。至少在某种程度上,授权、验证、异常处理、日志、本地化、数据库连接管理、配置管理、审计日志属于通用的结构。
另外我们总是在构建体系结构和最佳实践,比如分层和模块化架构,领域驱动设计,依赖注入等等,同时也在尝试基于惯例来开发应用。这些都是非常耗时且难于对每个项目独立构建。所以许多公司会构建自己的框架,他们用这些框架更快速、更少BUG地开发新应用。但是并不是所有公司都这么幸运,他们大部分都没有时间、预算、团队来开发这类框架。即便他们可能做出一个框架来,写文档,培训开发者以及维护也是很难的。
ASP.NET Boilerplate (ABP) 是一个开源的、文档完善的应用框架,基于“为所有公司和所有开发者开发一个通用的框架”这一目标,它不仅仅是一个框架,也是一个基于DDD和最佳实践的健壮的体系模型。
由一个简单类来探究ABP的优势
public class TaskAppService : ApplicationService, ITaskAppService
{
private readonly IRepository<Task> _taskRepository;
public TaskAppService(IRepository<Task> taskRepository)
{
_taskRepository = taskRepository;
}
[AbpAuthorize(MyPermissions.UpdatingTasks)]
public async Task UpdateTask(UpdateTaskInput input)
{
Logger.Info("Updating a task for input: " + input);
var task = await _taskRepository.FirstOrDefaultAsync(input.TaskId);
if (task == null)
{
throw new UserFriendlyException(L("CouldNotFoundTheTaskMessage"));
}
input.MapTo(task);
}
}
UpdateTask是一个简单的应用服务类的方法。在DDD中一个应用服务,会直接被表现层调用来实现应用的用例,比如它可能会被JavaScript通过AJAX调用。
总结这个类中的一些便利实现:
依赖注入 Dependency Injection
ABP使用并提供了一个健壮而又惯例式的DI基础设施。因为上面的类是一个应用服务,所以它会按照惯例约定临时地(每个请求创建一次)注册到DI容器中。它也简单地注入了所有依赖(本例中注入了IRepository)。
仓储 Repository
ABP可以为每一个实体创建一个默认的仓储(本例中是IRepository<Task>)。默认的仓储有许多有用的方法,如本例中的 FirstOrDefault。我们也可以根据自己的需求很容易地扩展这个默认仓储。仓储抽象了DBMS(数据库管理系统)和ORMs(对象关系映射),并简化了数据的访问逻辑。
授权 Authorization
ABP可以检查权限。如果当前的用户没有“updating task”权限或者没登录,那么他对UpdateTask方法的访问会被阻止。声明式的特性简化了授权方式,但其他的授权方法也一样可以使用。
验证 Validation
ABP会自动校验输入是否为null。它也基于标准的数据注解特性和自定义的验证规则验证输入对象的所有属性。如果请求不合法,那么它会抛出一个合适的验证异常。
审计日志 Audit Logging
基于惯例和配置为每个请求自动地保存 用户,浏览器,IP地址,调用服务,方法,参数,调用时间,执行时长和其他的一些信息。
工作单元 Unit Of Work
在ABP中,每个应用服务方法默认地视作是一个工作单元。它会自动创建一个连接并在方法的开始位置开启一个事务。如果方法不报异常地顺利执行了,那么事务会提交,连接也会释放。即使该方法使用了不同的仓储或者方法,它们也全部都是原子的(事务的)。当事务提交时,实体的所有改变都会自动保存。因此,正如方法实现的那样,我们甚至都不需要调用“_repository.Update(task)”方法。
异常处理 Exception Handling
在一个使用了ABP框架的Web应用中,我们基本上不用处理异常。所有的异常都会默认自动处理。如果一个异常发生了,那么ABP会自动地记录它,然后返回给客户端一个合适的结果。比如,如果这是一个AJAX请求,那么它会返回一个JSON到客户端,指明发生了一个错误。本例中使用了一个UserFriendlyException,这样就隐藏了客户端实际的异常信息。它也能理解并处理客户端的错误,最后将合适的信息呈现给用户。
日志 Logging
就像看到的,我们使用基类中定义的Logger对象来记录日志。默认使用Log4Net,但这是可以改变和配置的。
本地化 Localization
主义到我们在抛出异常时使用了L方法,它会基于当前用户的文化自动进行本地化。当然,我们会在某些地方定义CouldNotFoundTheTaskMessage(视本地化文档的实现而定)。
自动映射 Auto Mapping
在最后一行代码,我们使用了ABP的MapTo扩展方法将输入对象的属性映射到实体属性。它使用了AutoMapper库来执行映射。因此,我们可以基于命名惯例轻易地将属性从一个对象上映射到另一个对象上。
自动Web API层 Dynamic Web API Layer
实际上,TaskAppService 是一个简单的类(甚至不需要从ApplicationService 继承)。我们一般会写一个Web API Controller包装器来将方法暴露给javascript客户端,ABP在运行时会自动完成这些。这样,我们可以从客户端直接使用应用服务方法。
动态Ajax代理 Dynamic Javascript AJAX Proxy
ABP创建了javascript代理方法,它们可以调用应用服务方法就像调用客户端的javascript方法一样简单。
在这么一个简单的类中,我们看到了ABP的优势。所有的这些任务正常情况下都是要花费很多时间的,但是所有的这些ABP自动帮我们完成了。
其他
除了上面例子中展示的, ABP还提供了一个健壮的基础设施和应用模型。下面是ABP的一下其他特征:
模块化 Modularity
Provides a strong infrastructure to build reusable modules.
数据过滤 Data Filters
Provides automatic data filtering to implement some patterns like soft-delete and multi-tenancy.
多租户 Multi Tenancy
It fully supports multi-tenancy, including single database or database per tenant architectures.
配置管理 Setting Management
Provides a strong infrastructure to get/change application, tenant and user level settings.
单元&集成测试 Unit & Integration Testing
It's built testability in mind. Also provides base classes to simplify unit & integration tests. See this article for more information.
更多特征,请阅读文档。
启动模板
创建一个新的解决方案,创建层,安装nuget包,创建爱一个简单的布局和菜单……,所有这些是非常耗时的。
ABP提供了一个预生成的启动模板,它让创建一个新的解决方案变得更加容易。模板支持SPA(单页应用)和MPA(多页应用)。而且,我们可以选择不同的ORM。
使用
ABP的源码已经推送到了Github 上,Nuget包也已经发布到了Nuget上。开始使用ABP最简单的方式就是使用ABP官网的模板创建项目,然后跟着文档来学习。