首页 后端开发 C#.Net教程 ASP.NET样板开发框架ABP系列之ABP入门教程详解

ASP.NET样板开发框架ABP系列之ABP入门教程详解

Oct 14, 2017 am 10:00 AM
asp.net 框架 系列

ABP是为新的现代Web应用程序使用最佳实践和使用最流行工具的一个起点。可作为一般用途的应用程序的基础框架或项目模板。接下来通过本文给大家详细介绍ABP入门教程,感兴趣的朋友一起看看吧

ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。

ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点,它旨在成为一个通用的WEB应用程序框架和项目模板。

ABP的官方网站:http://www.aspnetboilerplate.com

ABP在Github上的开源项目:https://github.com/aspnetboilerplate

ABP 的由来

“DRY——避免重复代码”是一个优秀的开发者在开发软件时所具备的最重要的思想之一。我们在开发企业WEB应用程序时都有一些类似的需求,例如:都需要登录页面、用户/角色管理、权限验证、数据有效性验证、多语言/本地化等等。一个高品质的大型软件都会运用一些最佳实践,例如分层体系结构、领域驱动设计、依赖注入等。我们也可能会采用ORM、数据库迁移(Database Migrations)、日志记录(Logging)等工具。

从零开始创建一个企业应用程序是一件繁琐的事,因为需要重复做很多常见的基础工作。许多公司都在开发自己的应用程序框架来重用于不同的项目,然后在框架的基础上开发一些新的功能。但并不是每个公司都有这样的实力。假如我们可以分享的更多,也许可以避免每个公司或每个项目的重复编写类似的代码。作者之所以把项目命名为“ASP.NET Boilerplate”,就是希望它能成为开发一般企业WEB应用的新起点,直接把ABP作为项目模板。

ABP是什么?

ABP是为新的现代Web应用程序使用最佳实践和使用最流行工具的一个起点。可作为一般用途的应用程序的基础框架或项目模板。它的功能包括:

服务器端:

  • 基于最新的.NET技术 (目前是ASP.NET MVC 5、Web API 2、C# 5.0,在ASP.NET 5正式发布后会升级)

  • 实现领域驱动设计(实体、仓储、领域服务、领域事件、应用服务、数据传输对象,工作单元等等)

  • 实现分层体系结构(领域层,应用层,展现层和基础设施层)提供了一个基础架构来开发可重用可配置的模块集成一些最流行的开源框架/库,也许有些是你正在使用的。

  • 提供了一个基础架构让我们很方便地使用依赖注入(使用Castle Windsor作为依赖注入的容器)

  • 提供Repository仓储模式支持不同的ORM(已实现Entity Framework 、NHibernate、MangoDb和内存数据库)

  • 支持并实现数据库迁移(EF 的 Code first)模块化开发(每个模块有独立的EF DbContext,可单独指定数据库)

  • 包括一个简单的和灵活的多语言/本地化系统

  • 包括一个 EventBus来实现服务器端全局的领域事件统一的异常处理(应用层几乎不需要处理自己写异常处理代码)

  • 数据有效性验证(Asp.NET MVC只能做到Action方法的参数验证,ABP实现了Application层方法的参数有效性验证)

  • 通过Application Services自动创建Web Api层(不需要写ApiController层了)

  • 提供基类和帮助类让我们方便地实现一些常见的任务

  • 使用“约定优于配置原则”

客户端:

  • Bootstrap、Less、AngularJs、jQuery、Modernizr和其他JS库: jQuery.validate、jQuery.form、jQuery.blockUI、json2等

  • 为单页面应用程序(AngularJs、Durandaljs)和多页面应用程序(Bootstrap+Jquery)提供了项目模板。

  • 自动创建Javascript 的代理层来更方便使用Web Api封装一些Javascript 函数,更方便地使用ajax、消息框、通知组件、忙状态的遮罩层等等

除ABP框架项目以外,还开发了名叫“Zero”的模块,实现了以下功能:

  • 身份验证与授权管理(通过ASP.NET Identity实现的)

  • 用户&角色管理系统设置存取管理(系统级、租户级、用户级,作用范围自动管理)

  • 审计日志(自动记录每一次接口的调用者和参数)

ABP不是什么?

ABP提供了一个应用程序开发模型用于最佳实践。它拥有基础类、接口和工具使我们容易建立起可维护的大规模的应用程序。

然而:

它不是RAD工具之一,RAD工具的目的是无需编码创建应用程序。相反,ABP提供了一种编码的最佳实践。

它不是一个代码生成工具。在运行时虽然它有一些特性构建动态代码,但它不能生成代码。

它不是一个一体化的框架。相反,它使用流行的工具/库来完成特定的任务(例如用EF做ORM,用Log4Net做日志记录,使得Castle Windsor作为赖注入容器, AngularJs 用于SPA 框架)。

就我使用了ABP几个月的经验来看,虽然ABP不是RAD,但是用它开发项目绝对比传统三层架构要快很多。

虽然ABP不是代码生成工具,但因为有了它,使我们项目的代码更简洁规范,这有利于使用代码生成工具。

我自己使用VS2013的Scaffolder+T4开发的代码生成器,可根据领域对象的UML类图自动生成全部前后端代码和数据库,简单的CURD模块几乎不需要编写代码,有复杂业务逻辑的模块主要补充领域层代码即可。这样就能把时间多花在领域模型的设计上,减少写代码的时间。

下面通过原作者的“简单任务系统”例子,演示如何运用ABP开发项目

从模板创建空的web应用程序

ABP提供了一个启动模板用于新建的项目(尽管你能手动地创建项目并且从nuget获得ABP包,模板的方式更容易)。

转到www.aspnetboilerplate.com/Templates从模板创建你的应用程序。

你可以选择SPA(AngularJs或DurandalJs)或者选择MPA(经典的多页面应用程序)项目。可以选择Entity Framework或NHibernate作为ORM框架。

这里我们选择AngularJs和Entity Framework,填入项目名称“SimpleTaskSystem”,点击“CREATE MY PROJECT”按钮可以下载一个zip压缩包,解压后得到VS2013的解决方案,使用的.NET版本是 4.5.1。

每个项目里引用了Abp组件和其他第三方组件,需要从Nuget下载。

黄色感叹号图标,表示这个组件在本地文件夹中不存在,需要从Nuget上还原。操作如下:

要让项目运行起来,还得创建一个数据库。这个模板假设你正在使用SQL2008或者更新的版本。当然也可以很方便地换成其他的关系型数据库。

打开Web.Config文件可以查看和配置链接字符串:


<add name="Default" connectionString="Server=localhost; Database=SimpleTaskSystemDb; Trusted_Connection=True;" />
登录后复制

(在后面用到EF的Code first数据迁移时,会自动在SQL Server数据库中创建一个名为SimpleTaskSystemDb的数据库。)

就这样,项目已经准备好运行了!打开VS2013并且按F5:

下面将逐步实现这个简单的任务系统程序

创建实体

把实体类写在Core项目中,因为实体是领域层的一部分。

一个简单的应用场景:创建一些任务(tasks)并分配给人。 我们需要Task和Person这两个实体。

Task实体有几个属性:描述(Description)、创建时间(CreationTime)、任务状态(State),还有可选的导航属性(AssignedPerson)来引用Person。


public class Task : Entity<long>
{
 [ForeignKey("AssignedPersonId")]
 public virtual Person AssignedPerson { get; set; }

 public virtual int? AssignedPersonId { get; set; }

 public virtual string Description { get; set; }

 public virtual DateTime CreationTime { get; set; }

 public virtual TaskState State { get; set; }

 public Task()
 {
  CreationTime = DateTime.Now;
  State = TaskState.Active;
 }
}
登录后复制

Person实体更简单,只定义了一个Name属性:


public class Person : Entity
{
 public virtual string Name { get; set; }
}
登录后复制

在ABP框架中,有一个Entity基类,它有一个Id属性。因为Task类继承自Entity,所以它有一个long类型的Id。Person类有一个int类型的Id,因为int类型是Entity基类Id的默认类型,没有特别指定类型时,实体的Id就是int类型。

创建DbContext

使用EntityFramework需要先定义DbContext类,ABP的模板已经创建了DbContext文件,我们只需要把Task和Person类添加到IDbSet,请看代码:


public class SimpleTaskSystemDbContext : AbpDbContext
{
 public virtual IDbSet<Task> Tasks { get; set; }

 public virtual IDbSet<Person> People { get; set; }

 public SimpleTaskSystemDbContext()
  : base("Default")
 {

 }

 public SimpleTaskSystemDbContext(string nameOrConnectionString)
  : base(nameOrConnectionString)
 {
   
 }
}
登录后复制

通过Database Migrations创建数据库表

我们使用EntityFramework的Code First模式创建数据库架构。ABP模板生成的项目已经默认开启了数据迁移功能,我们修改SimpleTaskSystem.EntityFramework项目下Migrations文件夹下的Configuration.cs文件:


internal sealed class Configuration : DbMigrationsConfiguration<SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext>
{
 public Configuration()
 {
  AutomaticMigrationsEnabled = false;
 }

 protected override void Seed(SimpleTaskSystem.EntityFramework.SimpleTaskSystemDbContext context)
 {
  context.People.AddOrUpdate(
   p => p.Name,
   new Person {Name = "Isaac Asimov"},
   new Person {Name = "Thomas More"},
   new Person {Name = "George Orwell"},
   new Person {Name = "Douglas Adams"}
   );
 }
}
登录后复制

在VS2013底部的“程序包管理器控制台”窗口中,选择默认项目并执行命令“Add-Migration InitialCreate”

会在Migrations文件夹下生成一个xxxx-InitialCreate.cs文件,内容如下:


public partial class InitialCreate : DbMigration
{
 public override void Up()
 {
  CreateTable(
   "dbo.StsPeople",
   c => new
    {
     Id = c.Int(nullable: false, identity: true),
     Name = c.String(),
    })
   .PrimaryKey(t => t.Id);
   
  CreateTable(
   "dbo.StsTasks",
   c => new
    {
     Id = c.Long(nullable: false, identity: true),
     AssignedPersonId = c.Int(),
     Description = c.String(),
     CreationTime = c.DateTime(nullable: false),
     State = c.Byte(nullable: false),
    })
   .PrimaryKey(t => t.Id)
   .ForeignKey("dbo.StsPeople", t => t.AssignedPersonId)
   .Index(t => t.AssignedPersonId);   
 }
  
 public override void Down()
 {
  DropForeignKey("dbo.StsTasks", "AssignedPersonId", "dbo.StsPeople");
  DropIndex("dbo.StsTasks", new[] { "AssignedPersonId" });
  DropTable("dbo.StsTasks");
  DropTable("dbo.StsPeople");
 }
}
登录后复制

然后继续在“程序包管理器控制台”执行“Update-Database”,会自动在数据库创建相应的数据表:


PM> Update-Database
登录后复制

数据库显示如下:

(以后修改了实体,可以再次执行Add-Migration和Update-Database,就能很轻松的让数据库结构与实体类的同步)

定义仓储接口

通过仓储模式,可以更好把业务代码与数据库操作代码更好的分离,可以针对不同的数据库有不同的实现类,而业务代码不需要修改。

定义仓储接口的代码写到Core项目中,因为仓储接口是领域层的一部分。

我们先定义Task的仓储接口:


public interface ITaskRepository : IRepository<Task, long>
{
登录后复制

它继承自ABP框架中的IRepository泛型接口。

在IRepository中已经定义了常用的增删改查方法:

所以ITaskRepository默认就有了上面那些方法。可以再加上它独有的方法GetAllWithPeople(...)。

不需要为Person类创建一个仓储类,因为默认的方法已经够用了。ABP提供了一种注入通用仓储的方式,将在后面“创建应用服务”一节的TaskAppService类中看到。

实现仓储类

我们将在EntityFramework项目中实现上面定义的ITaskRepository仓储接口。

通过模板建立的项目已经定义了一个仓储基类:SimpleTaskSystemRepositoryBase(这是一种比较好的实践,因为以后可以在这个基类中添加通用的方法)。


public class TaskRepository : SimpleTaskSystemRepositoryBase<Task, long>, ITaskRepository
{
  public List<Task> GetAllWithPeople(int? assignedPersonId, TaskState? state)
  {
    //在仓储方法中,不用处理数据库连接、DbContext和数据事务,ABP框架会自动处理。
      
    var query = GetAll(); //GetAll() 返回一个 IQueryable<T>接口类型
      
    //添加一些Where条件

    if (assignedPersonId.HasValue)
    {
      query = query.Where(task => task.AssignedPerson.Id == assignedPersonId.Value);
    }

    if (state.HasValue)
    {
      query = query.Where(task => task.State == state);
    }

    return query
      .OrderByDescending(task => task.CreationTime)
      .Include(task => task.AssignedPerson)
      .ToList();
  }
}
登录后复制

TaskRepository继承自SimpleTaskSystemRepositoryBase并且实现了上面定义的ITaskRepository接口。

创建应用服务(Application Services)

Application项目中定义应用服务。首先定义Task的应用服务层的接口:


public interface ITaskAppService : IApplicationService
{
  GetTasksOutput GetTasks(GetTasksInput input);
  void UpdateTask(UpdateTaskInput input);
  void CreateTask(CreateTaskInput input);
}
登录后复制

ITaskAppService继承自IApplicationService,ABP自动为这个类提供一些功能特性(比如依赖注入和参数有效性验证)。

然后,我们写TaskAppService类来实现ITaskAppService接口:


public class TaskAppService : ApplicationService, ITaskAppService
{
  private readonly ITaskRepository _taskRepository;
  private readonly IRepository<Person> _personRepository;
    
  /// <summary>
  /// 构造函数自动注入我们所需要的类或接口
  /// </summary>
  public TaskAppService(ITaskRepository taskRepository, IRepository<Person> personRepository)
  {
    _taskRepository = taskRepository;
    _personRepository = personRepository;
  }
    
  public GetTasksOutput GetTasks(GetTasksInput input)
  {
    //调用Task仓储的特定方法GetAllWithPeople
    var tasks = _taskRepository.GetAllWithPeople(input.AssignedPersonId, input.State);

    //用AutoMapper自动将List<Task>转换成List<TaskDto>
    return new GetTasksOutput
        {
          Tasks = Mapper.Map<List<TaskDto>>(tasks)
        };
  }
    
  public void UpdateTask(UpdateTaskInput input)
  {
    //可以直接Logger,它在ApplicationService基类中定义的
    Logger.Info("Updating a task for input: " + input);

    //通过仓储基类的通用方法Get,获取指定Id的Task实体对象
    var task = _taskRepository.Get(input.TaskId);

    //修改task实体的属性值
    if (input.State.HasValue)
    {
      task.State = input.State.Value;
    }

    if (input.AssignedPersonId.HasValue)
    {
      task.AssignedPerson = _personRepository.Load(input.AssignedPersonId.Value);
    }

    //我们都不需要调用Update方法
    //因为应用服务层的方法默认开启了工作单元模式(Unit of Work)
    //ABP框架会工作单元完成时自动保存对实体的所有更改,除非有异常抛出。有异常时会自动回滚,因为工作单元默认开启数据库事务。
  }

  public void CreateTask(CreateTaskInput input)
  {
    Logger.Info("Creating a task for input: " + input);

    //通过输入参数,创建一个新的Task实体
    var task = new Task { Description = input.Description };

    if (input.AssignedPersonId.HasValue)
    {
      task.AssignedPersonId = input.AssignedPersonId.Value;
    }

    //调用仓储基类的Insert方法把实体保存到数据库中
    _taskRepository.Insert(task);
  }
}
登录后复制

TaskAppService使用仓储进行数据库操作,它通往构造函数注入仓储对象的引用。

数据验证

如果应用服务(Application Service)方法的参数对象实现了IInputDto或IValidate接口,ABP会自动进行参数有效性验证。

CreateTask方法有一个CreateTaskInput参数,定义如下:


public class CreateTaskInput : IInputDto
{
  public int? AssignedPersonId { get; set; }

  [Required]
  public string Description { get; set; }
}
登录后复制

Description属性通过注解指定它是必填项。也可以使用其他 Data Annotation 特性。

如果你想使用自定义验证,你可以实现ICustomValidate 接口:


public class UpdateTaskInput : IInputDto, ICustomValidate
{
  [Range(1, long.MaxValue)]
  public long TaskId { get; set; }

  public int? AssignedPersonId { get; set; }

  public TaskState? State { get; set; }

  public void AddValidationErrors(List<ValidationResult> results)
  {
    if (AssignedPersonId == null && State == null)
    {
      results.Add(new ValidationResult("AssignedPersonId和State不能同时为空!", new[] { "AssignedPersonId", "State" }));
    }
  }
}
登录后复制

你可以在AddValidationErrors方法中写自定义验证的代码。

创建Web Api服务

ABP可以非常轻松地把Application Service的public方法发布成Web Api接口,可以供客户端通过ajax调用。


DynamicApiControllerBuilder
  .ForAll<IApplicationService>(Assembly.GetAssembly(typeof (SimpleTaskSystemApplicationModule)), "tasksystem")
  .Build();
登录后复制

SimpleTaskSystemApplicationModule这个程序集中所有继承了IApplicationService接口的类,都会自动创建相应的ApiController,其中的公开方法,就会转换成WebApi接口方法。

可以通过http://xxx/api/services/tasksystem/Task/GetTasks这样的路由地址进行调用。

通过上面的案例,大致介绍了领域层、基础设施层、应用服务层的用法。

现在,可以在ASP.NET MVC的Controller的Action方法中直接调用Application Service的方法了。

如果用SPA单页编程,可以直接在客户端通过ajax调用相应的Application Service的方法了(通过创建了动态Web Api)。

总结

以上是ASP.NET样板开发框架ABP系列之ABP入门教程详解的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
4 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.聊天命令以及如何使用它们
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

小米 15 系列全代号曝光:Dada、Haotian、Xuanyuan 小米 15 系列全代号曝光:Dada、Haotian、Xuanyuan Aug 22, 2024 pm 06:47 PM

小米15系列预计将于10月份正式发布,其全系列代号已在外媒MiCode代码库中曝光。其中,旗舰级小米15Ultra代号为"Xuanyuan"(意为"轩辕"),此名源自中国神话中的黄帝,象征着尊贵。小米15的代号为"Dada",而小米15Pro则以"Haotian"(意为"昊天")为名。小米15SPro内部代号为"dijun",暗指《山海经》创世神帝俊。小米15Ultra系列涵盖

华为 Mate 60 系列最佳入手时机,新增 AI 消除 + 影像升级,更可享秋日礼遇活动 华为 Mate 60 系列最佳入手时机,新增 AI 消除 + 影像升级,更可享秋日礼遇活动 Aug 29, 2024 pm 03:33 PM

自去年华为Mate60系列开售以来,我个人就一直将Mate60Pro作为主力机使用。在将近一年的时间里,华为Mate60Pro经过多次OTA升级,综合体验有了显着提升,给人一种常用常新的感觉。比如近期,华为Mate60系列就再度迎来了影像功能的重磅升级。首先是新增AI消除功能,可以智能消除路人、杂物并对空白部分进行自动补充;其次是主摄色准、长焦清晰度均有明显升级。考虑到现在是开学季,华为Mate60系列还推出了秋日礼遇活动:购机可享至高800元优惠,入手价低至4999元。常用常新的产品力加上超值

如何评估Java框架商业支持的性价比 如何评估Java框架商业支持的性价比 Jun 05, 2024 pm 05:25 PM

评估Java框架商业支持的性价比涉及以下步骤:确定所需的保障级别和服务水平协议(SLA)保证。研究支持团队的经验和专业知识。考虑附加服务,如升级、故障排除和性能优化。权衡商业支持成本与风险缓解和提高效率。

PHP 框架的学习曲线与其他语言框架相比如何? PHP 框架的学习曲线与其他语言框架相比如何? Jun 06, 2024 pm 12:41 PM

PHP框架的学习曲线取决于语言熟练度、框架复杂性、文档质量和社区支持。与Python框架相比,PHP框架的学习曲线更高,而与Ruby框架相比,则较低。与Java框架相比,PHP框架的学习曲线中等,但入门时间较短。

PHP 框架的轻量级选项如何影响应用程序性能? PHP 框架的轻量级选项如何影响应用程序性能? Jun 06, 2024 am 10:53 AM

轻量级PHP框架通过小体积和低资源消耗提升应用程序性能。其特点包括:体积小,启动快,内存占用低提升响应速度和吞吐量,降低资源消耗实战案例:SlimFramework创建RESTAPI,仅500KB,高响应性、高吞吐量

golang框架文档最佳实践 golang框架文档最佳实践 Jun 04, 2024 pm 05:00 PM

编写清晰全面的文档对于Golang框架至关重要。最佳实践包括:遵循既定文档风格,例如Google的Go编码风格指南。使用清晰的组织结构,包括标题、子标题和列表,并提供导航。提供全面准确的信息,包括入门指南、API参考和概念。使用代码示例说明概念和使用方法。保持文档更新,跟踪更改并记录新功能。提供支持和社区资源,例如GitHub问题和论坛。创建实际案例,如API文档。

如何为不同的应用场景选择最佳的golang框架 如何为不同的应用场景选择最佳的golang框架 Jun 05, 2024 pm 04:05 PM

根据应用场景选择最佳Go框架:考虑应用类型、语言特性、性能需求、生态系统。常见Go框架:Gin(Web应用)、Echo(Web服务)、Fiber(高吞吐量)、gorm(ORM)、fasthttp(速度)。实战案例:构建RESTAPI(Fiber),与数据库交互(gorm)。选择框架:性能关键选fasthttp,灵活Web应用选Gin/Echo,数据库交互选gorm。

golang框架开发实战详解:问题答疑 golang框架开发实战详解:问题答疑 Jun 06, 2024 am 10:57 AM

在Go框架开发中,常见的挑战及其解决方案是:错误处理:利用errors包进行管理,并使用中间件集中处理错误。身份验证和授权:集成第三方库并创建自定义中间件来检查凭据。并发处理:利用goroutine、互斥锁和通道来控制资源访问。单元测试:使用gotest包,模拟和存根进行隔离,并使用代码覆盖率工具确保充分性。部署和监控:使用Docker容器打包部署,设置数据备份,通过日志记录和监控工具跟踪性能和错误。

See all articles