C# Abp框架入门系列文章(一)
随着技术的进步,各式各样的框架层出不穷,轮子越来越多,那么有没有哪些优秀的开发框架供我们使用呢?如果我们能够将各方面优秀的框架集合起来,应用到项目开发中,我们的工作是不是能事半功倍呢?而且各个框架的使用方向不同,很多配置也不同,如果能够将繁杂的基础工作集成起来,由统一的框架来完成,那么我们就可以专注于业务逻辑,提高工作效率。现在Abp就是这么一个框架,使用流行技术开发现代web应用程序的最佳实践。本文作为Abp框架的入门文章,仅供学习分享使用,如有不足之处,还请指正。
什么是Abp?
ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。
ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点,它旨在成为一个通用的WEB应用程序框架和项目模板。ABP是基于最新的ASP.NET CORE,ASP.NET MVC和Web API技术的应用程序框架。并使用流行的框架和库,它提供了便于使用的授权,依赖注入,验证,异常处理,本地化,日志记录,缓存等常用功能。
Abp架构
ABP实现了多层架构(领域层,应用层,基础设施层和表示层),以及领域驱动设计(实体,存储库,领域服务,应用程序服务,DTO等)。还实现和提供了良好的基础设施来实现最佳实践,如依赖注入。
了解了Abp框架的基础知识之后,让我们一步一步的搭建Abp框架,并实现一个简单的小例子。
安装CLI
输入cmd打开命令行窗口,然后输入以下命令,安装Abp.Cli,如下所示:
dotnet tool install -g Volo.Abp.Cli
安装过程,如下图所示:
创建第一个Abp项目
在命令行,切换到程序所在目录【最好是空目录】,然后通过命令进行创建,如下所示:
abp new Acme.BookStore
安装过程,如下图所示:
关于Abp的Cli相关命令,可参考官方文档 。
项目创建成功后,如下所示:
通过Visual Studio打开解决方案,如下所示:
还原数据库
在Abp解决方案中,通过运行【Acme.BookStore.DbMigrator】进行初始化数据库。该项目是控制台程序,采用Entity Framework的Code First方式迁移数据库。
打开项目【Acme.BookStore.DbMigrator】中 appsettings.json文件,修改数据库连接字符串,为本机连接字符串,如下所示:
将项目设置为启动项目,然后F5(或Ctrl + F5)运行即可。当出现以下页面时,表示数据库迁移成功。如下所示:
数据库还原成功后,打开SQL Server数据库,会多出一个数据库【BookStore】,如下所示:
注意:最新版本的Abp版本为5.0.0,支持的Entity Framework Core版本为6.0,目前已不再支持SQL Server 2008 R2。所以需要升级数据库版本到2012。
运行Abp程序
打开项目【Acme.BookStore.Web】中的appsettings.json文件,修改数据库连接字符串,如下所示:
将项目【Acme.BookStore.Web】设置为启动项目,然后按F5(或Ctrl+F5)运行项目。Visual Studio会自动打开首页【https://localhost:44327/】,如下所示:
在首页上,点击登录【默认用户名 admin,密码 1q2w3E* 】,如下所示:
登录成功后,如下所示:
以上就是Abp最新默认框架示例。接下来让我们一起开发一个图书管理的小功能。
Abp入门示例
1. 创建Book实体类
启动模板中的领域层分为两个项目:
Acme.BookStore.Domain
包含你的实体, 领域服务和其他核心域对象.Acme.BookStore.Domain.Shared
包含可与客户共享的常量,枚举或其他域相关对象.
在解决方案的领域层(Acme.BookStore.Domain
项目)中定义实体,如下所示:
在Acme.BookStore.Domain项目中,右键创建文件夹Books,然后新增Book类,如下所示:
namespace Acme.BookStore.Books
{public class Book : AuditedAggregateRoot<Guid>{public string Name { get; set; }public BookType Type { get; set; }public DateTime PublishDate { get; set; }public float Price { get; set; }}
}
其中Book继承自AuditedAggregateRoot<Guid>。在Abp中,默认提供了两个实体基类AggregateRoot
和Entity,而AuditedAggregateRoot<Guid>是AggregateRoot的派生类。其中Guid是主键类型。
上述类中用到的BookType为创建的 枚举类型,在Acme.BookStore.Domain.Shared项目中,如下所示:
namespace Acme.BookStore.Books
{public enum BookType{Undefined,Adventure,Biography,Dystopia,Fantastic,Horror,Science,ScienceFiction,Poetry}
}
Book和BookType,如下所示:
2. 将Book实体添加到DbContext中
EF Core需要你将实体和 DbContext
建立关联.最简单的做法是在Acme.BookStore.EntityFrameworkCore
项目的BookStoreDbContext
类中添加DbSet
属性.如下所示:
namespace Acme.BookStore.EntityFrameworkCore
{[ReplaceDbContext(typeof(IIdentityDbContext))][ReplaceDbContext(typeof(ITenantManagementDbContext))][ConnectionStringName("Default")]public class BookStoreDbContext :AbpDbContext<BookStoreDbContext>,IIdentityDbContext,ITenantManagementDbContext{/* Add DbSet properties for your Aggregate Roots / Entities here. */#region Entities from the modules//其他自带的已略去/// <summary>/// Book示例数据库操作/// </summary>public DbSet<Book> Books { get; set; }#endregion}
}
3. 将Book实体映射到数据库表
在本示例中采用Code First方式自动生成数据库,所以需要将实体和数据库表进行映射。在 Acme.BookStore.EntityFrameworkCore
项目中打开 BookStoreDbContextModelCreatingExtensions.cs
文件,添加 Book
实体的映射代码. 最终类应为:
namespace Acme.BookStore.EntityFrameworkCore
{public static class BookStoreDbContextModelCreatingExtensions{public static void ConfigureBookStore(this ModelBuilder builder){Check.NotNull(builder, nameof(builder));/* Configure your own tables/entities inside here */builder.Entity<Book>(b =>{b.ToTable(BookStoreConsts.DbTablePrefix + "Books",BookStoreConsts.DbSchema);b.ConfigureByConvention(); //auto configure for the base class propsb.Property(x => x.Name).IsRequired().HasMaxLength(128);});}}
}
BookStoreConsts
含有用于表的架构和表前缀的常量值. 你不必使用它,但建议在单点控制表前缀.ConfigureByConvention()
方法优雅的配置/映射继承的属性,应始终对你所有的实体使用它.
3. 添加数据迁移
启动模板使用EF Core Code First Migrations创建和维护数据库架构. 我们应该创建一个新的迁移并且应用到数据库.
在 Acme.BookStore.EntityFrameworkCore
目录打开命令行终端输入以下命令:
dotnet ef migrations add Created_Book_Entity
具体示例如下所示:
上述命令,会添加新的迁移类到项目中,如下所示:
4. 添加种子数据
如果不需要通过代码添加种子数据,可以跳过,建议遵循步骤操作,以熟悉Abp框架。在 Acme.BookStore.Domain 项目下创建派生 IDataSeedContributor
的类,并且拷贝以下代码:
namespace Acme.BookStore.Books
{public class BookStoreDataSeederContributor: IDataSeedContributor, ITransientDependency{private readonly IRepository<Book, Guid> _bookRepository;public BookStoreDataSeederContributor(IRepository<Book, Guid> bookRepository){_bookRepository = bookRepository;}public async Task SeedAsync(DataSeedContext context){if (await _bookRepository.GetCountAsync() <= 0){await _bookRepository.InsertAsync(new Book{Name = "1984",Type = BookType.Dystopia,PublishDate = new DateTime(1949, 6, 8),Price = 19.84f},autoSave: true);await _bookRepository.InsertAsync(new Book{Name = "The Hitchhiker's Guide to the Galaxy",Type = BookType.ScienceFiction,PublishDate = new DateTime(1995, 9, 27),Price = 42.0f},autoSave: true);}}}
}
- 如果数据库中当前没有图书,则此代码使用
IRepository<Book, Guid>
(默认为repository)将两本书插入数据库.
5. 更新数据库
运行 Acme.BookStore.DbMigrator
应用程序来更新数据库,将Acme.BookStore.DbMigrator设置为启动程序,然后运行即可,如下所示:
执行成功后,打开数据库管理工具,即可看到新生成的数据表,如下所示:
以上则表示数据库创建成功。
6. 创建应用程序
应用程序层由两个分离的项目组成:
Acme.BookStore.Application.Contracts
包含你的DTO和应用服务接口.Acme.BookStore.Application
包含你的应用服务实现.
在本部分中,创建一个应用程序服务,使用ABP Framework的 CrudAppService
基类来获取,创建,更新和删除书籍.
6. 1 创建BookDto类
在Abp中,需要创建Book实体的Dto类,在Acme.BookStore.Application.Contracts项目中,添加BootDto类,如下所示:
namespace Acme.BookStore
{public class BookDto : AuditedEntityDto<Guid>{public string Name { get; set; }public BookType Type { get; set; }public DateTime PublishDate { get; set; }public float Price { get; set; }}
}
- DTO类被用来在 表示层 和 应用层 传递数据.
- 为了在页面上展示书籍信息,
BookDto
被用来将书籍数据传递到表示层. BookDto
继承自AuditedEntityDto<Guid>
.跟上面定义的Book
实体一样具有一些审计属性.
在将书籍返回到表示层时,需要将Book
实体转换为BookDto
对象. AutoMapper库可以在定义了正确的映射时自动执行此转换. 启动模板配置了AutoMapper,因此你只需在Acme.BookStore.Application
项目的BookStoreApplicationAutoMapperProfile
类中定义映射:
namespace Acme.BookStore
{public class BookStoreApplicationAutoMapperProfile : Profile{public BookStoreApplicationAutoMapperProfile(){/* You can configure your AutoMapper mapping configuration here.* Alternatively, you can split your mapping configurations* into multiple profile classes for a better organization. */CreateMap<Book, BookDto>();}}
}
6.2 CreateUpdateBookDto
在Acme.BookStore.Application.Contracts
项目中创建一个名为 CreateUpdateBookDto
的DTO类:
namespace Acme.BookStore.Books
{public class CreateUpdateBookDto{[Required][StringLength(128)]public string Name { get; set; }[Required]public BookType Type { get; set; } = BookType.Undefined;[Required][DataType(DataType.Date)]public DateTime PublishDate { get; set; } = DateTime.Now;[Required]public float Price { get; set; }}
}
- 这个DTO类被用于在创建或更新书籍的时候从用户界面获取图书信息.
- 它定义了数据注释属性(如
[Required]
)来定义属性的验证. DTO由ABP框架自动验证.
就像上面的BookDto
一样,创建一个从CreateUpdateBookDto
对象到Book
实体的映射,最终映射配置类如下:
namespace Acme.BookStore
{public class BookStoreApplicationAutoMapperProfile : Profile{public BookStoreApplicationAutoMapperProfile(){/* You can configure your AutoMapper mapping configuration here.* Alternatively, you can split your mapping configurations* into multiple profile classes for a better organization. */CreateMap<Book, BookDto>();CreateMap<CreateUpdateBookDto, Book>();}}
}
7. 创建应用程序服务
7.1 创建IBookAppService
下一步是为应用程序定义接口,在Acme.BookStore.Application.Contracts
项目中定义一个名为IBookAppService
的接口:
- 框架定义应用程序服务的接口不是必需的. 但是,它被建议作为最佳实践.
ICrudAppService
定义了常见的CRUD方法:GetAsync
,GetListAsync
,CreateAsync
,UpdateAsync
和DeleteAsync
. 你可以从空的IApplicationService
接口继承并手动定义自己的方法(将在下一部分中完成).ICrudAppService
有一些变体, 你可以在每个方法中使用单独的DTO,也可以分别单独指定(例如使用不同的DTO进行创建和更新).
7.2 创建 BookAppService
在Acme.BookStore.Application
项目中创建名为 BookAppService
的 IBookAppService
实现:
namespace Acme.BookStore.Books
{public class BookAppService :CrudAppService<Book, //The Book entityBookDto, //Used to show booksGuid, //Primary key of the book entityPagedAndSortedResultRequestDto, //Used for paging/sortingCreateUpdateBookDto>, //Used to create/update a bookIBookAppService //implement the IBookAppService{public BookAppService(IRepository<Book, Guid> repository): base(repository){}}
}
BookAppService
继承了CrudAppService<...>
.它实现了ICrudAppService
定义的CRUD方法.BookAppService
注入IRepository <Book,Guid>
,这是Book
实体的默认仓储. ABP自动为每个聚合根(或实体)创建默认仓储. 请参阅仓储文档BookAppService
使用IObjectMapper
将Book
对象转换为BookDto
对象, 将CreateUpdateBookDto
对象转换为Book
对象. 启动模板使用AutoMapper库作为对象映射提供程序. 我们之前定义了映射, 因此它将按预期工作.
8. 自动生成API Controllers
你通常创建Controller以将应用程序服务公开为HTTP API端点. 因此允许浏览器或第三方客户端通过AJAX调用它们.
ABP可以自动按照惯例将你的应用程序服务配置为MVC API控制器.
9. Swagger UI
启动模板配置为使用Swashbuckle.AspNetCore运行swagger UI. 运行应用程序并在浏览器中输入https://localhost:XXXX/swagger/
(用你自己的端口替换XXXX)作为URL.
你会看到一些内置的接口和Book
的接口,它们都是REST风格的:
10. 创建页面
在Acme.BookStore.Web项目的Pages文件夹下,创建Books目录,然后新增Razer Pages,如下所示:
添加成功后,如下所示:
Index.cshtml页面代码如下所示:
@page
@using Acme.BookStore.Web.Pages.Books
@model IndexModel<h2>Books</h2>
11. 将Book页面添加到主菜单
打开 Menus
文件夹中的 BookStoreMenuContributor
类,在 ConfigureMainMenuAsync
方法的底部添加如下代码:
namespace Acme.BookStore.Web.Menus
{public class BookStoreMenuContributor : IMenuContributor{public async Task ConfigureMenuAsync(MenuConfigurationContext context){if (context.Menu.Name == StandardMenus.Main){await ConfigureMainMenuAsync(context);}}private async Task ConfigureMainMenuAsync(MenuConfigurationContext context){var administration = context.Menu.GetAdministration();var l = context.GetLocalizer<BookStoreResource>();context.Menu.Items.Insert(0,new ApplicationMenuItem(BookStoreMenus.Home,l["Menu:Home"],"~/",icon: "fas fa-home",order: 0));if (MultiTenancyConsts.IsEnabled){administration.SetSubItemOrder(TenantManagementMenuNames.GroupName, 1);}else{administration.TryRemoveMenuItem(TenantManagementMenuNames.GroupName);}administration.SetSubItemOrder(IdentityMenuNames.GroupName, 2);administration.SetSubItemOrder(SettingManagementMenuNames.GroupName, 3);//添加book菜单context.Menu.AddItem(new ApplicationMenuItem("BooksStore",l["Menu:BookStore"],icon: "fa fa-book").AddItem(new ApplicationMenuItem("BooksStore.Books",l["Menu:Books"],url: "/Books")));}}
}
运行Acme.BookStore.Web项目,等录以后,便可以查看菜单,如下所示:
点击菜单后,跳转到默认的Book首页,如下所示:
12. 修改Book首页
将 Pages/Book/Index.cshtml
改成下面的样子:
@page
@using Acme.BookStore.Localization
@using Acme.BookStore.Web.Pages.Books
@using Microsoft.Extensions.Localization
@model IndexModel
@inject IStringLocalizer<BookStoreResource> L
@section scripts
{<abp-script src="/Pages/Books/Index.js" />
}
<abp-card><abp-card-header><h2>@L["Books"]</h2></abp-card-header><abp-card-body><abp-table striped-rows="true" id="BooksTable"></abp-table></abp-card-body>
</abp-card>
其中引用的Index.js位于Pages/Books目录下,如下所示:
$(function () {var l = abp.localization.getResource('BookStore');var dataTable = $('#BooksTable').DataTable(abp.libs.datatables.normalizeConfiguration({serverSide: true,paging: true,order: [[1, "asc"]],searching: false,scrollX: true,ajax: abp.libs.datatables.createAjax(acme.bookStore.books.book.getList),columnDefs: [{title: l('Name'),data: "name"},{title: l('Type'),data: "type",render: function (data) {return l('Enum:BookType:' + data);}},{title: l('PublishDate'),data: "publishDate",render: function (data) {return luxon.DateTime.fromISO(data, {locale: abp.localization.currentCulture.name}).toLocaleString();}},{title: l('Price'),data: "price"},{title: l('CreationTime'), data: "creationTime",render: function (data) {return luxon.DateTime.fromISO(data, {locale: abp.localization.currentCulture.name}).toLocaleString(luxon.DateTime.DATETIME_SHORT);}}]}));
});
然后运行项目,结果如下所示:
以上就是Abp的简单入门介绍,旨在抛转引玉,一起学习,共同进步。
可点击示例源码下载
备注
浣溪沙·一曲新词酒一杯【作者】晏殊 【朝代】北宋
一曲新词酒一杯,去年天气旧亭台。夕阳西下几时回?
无可奈何花落去,似曾相识燕归来。小园香径独徘徊。
C# Abp框架入门系列文章(一)相关推荐
- saltstack之基础入门系列文章简介
使用saltstack已有一段时间,最近由于各种原因,特来整理了saltstack基础入门系列文章,已备后续不断查阅(俗话说好记性不如烂笔头),也算是使用此工具的一个总结. saltstack的前六篇 ...
- ABP框架心得系列-1.本土化和ABP核心
ABP框架心得系列-1.本土化和ABP核心 ABP框架介绍:https://www.cnblogs.com/mienreal/p/4528641.html 系列导航帖:http://blog.csdn ...
- 基于ASP.NET MVC的ABP框架入门学习教程
为什么使用ABP 我们近几年陆续开发了一些Web应用和桌面应用,需求或简单或复杂,实现或优雅或丑陋.一个基本的事实是:我们只是积累了一些经验或提高了对,NET的熟悉程度. 随着软件开发经验的不断增加, ...
- 语言及框架演变系列文章:J2EE简介
本文章内容是从百度百科里转载的,因为觉得对J2EE的起源背景介绍的比较详细,所以先存入保存.最近在看HeadFirst系列书籍,差不多一周一本的节奏,进而衍生出很多内容,自己从开发到管理也做了很长时间 ...
- 流媒体入门系列文章(转自观止云公众号)
[流媒体|从入门到出家]:零,开始 https://mp.weixin.qq.com/s/oMbRtj9kcyBz059BRR2u4Q [流媒体|从入门到出家]:流媒体原理(上) https://mp ...
- DXUT框架剖析系列文章(原创:天行健 君子当自强而不息)
本文版权归博客园 lovedday 所有,转载请详细标明原创作者及原文出处,以示尊重! 原创作者: lovedday 原创博客:天行健 君子当自强而不息 原文出处:DXUT框架剖析 DXUT框架剖析 ...
- Flowable入门系列文章47 - 电子邮件任务
Flowable允许您通过向一个或多个收件人发送电子邮件的自动邮件服务任务来增强业务流程,包括支持cc,bcc,HTML内容等.请注意,邮件任务不是 BPMN 2.0规范的正式任务(因此没有专门的图标 ...
- Flowable入门系列文章195 - JMX的组态和服务URL
1.组态 JMX使用默认配置,以便于使用最常用的配置进行部署.但是,更改默认配置很容易.您可以通过编程或通过配置文件来完成.下面的代码片段显示了如何在配置文件中完成这个工作: <bean id= ...
- 理财入门:财务报表(简单介绍,后续入门系列文章写完后,会写实践文章在详细介绍)
文章目录 前言 财务报表是什么,有什么用? 上市公司的财报 通过财报读懂企业的18个步骤 系统的投资技能顺序 前言 选出优质生钱资产类型股票是离不开财报分析这项技能的,财报不仅和投资有关,还能分析出家 ...
- Flowable入门系列文章11 - Flowable API 01
1.Process Engine API和服务 引擎API是与Flowable进行交互的最常见的方式.主要的出发点是ProcessEngine,可以按照配置部分所述的几种方式创建 .从 Process ...
最新文章
- 一步步教你如何用疯狂.NET架构中的通用权限系统 -- 如何控制用户显示的菜单权限...
- C#实现人脸识别【Users】
- flask同源策略解决办法及flask-cors只允许特定域名跨域
- 项目Beta冲刺(团队)第七天
- 【SpringClould】 eureka 的自我保护机制
- 《Linux编程》上机作业 ·002【Linux常用工具GCC、GDB、Make】
- 1.2亿次下载,近3万Star的开源项目是为何会“死”掉?
- 读取模式错误、计算引擎操作复杂:这些Hadoop问题该如何应对?
- TabIndex 属性 Tabindex=-1 与Tabindex=0、任意数字 (收录)
- 软件测试之软件配置项测试
- EasyCVR调用Ehome协议接入的硬盘录像机设备录像无法播放,如何处理?
- 【Java加密】JCA体系结构
- index.highlight.max_analyzed_offset 偏移量设置
- 偏导数,全导数,方向导数,偏微分,全微分,梯度
- 专转本c语言刷题app,南京晓庄学院五年一贯制专转本c语言模拟考试2
- AN 非空检测以及影片剪辑元件调用内部元件
- 8051芯片选型 - 新唐MS51
- Linux下的网络配置
- 【恒指早盘分析】8.29恒指早盘思路
- 大学计算机学习计划书,大学学习计划书500字
热门文章
- hbase版本与Hadoop版本支持关系(官方)
- 潍坊学院计算机奖学金公示,潍坊学院2018-2019学年国家奖学金答辩会顺利举行
- 新零售电商O2O模式解析
- 读书笔记-捌-《创业维艰》
- ae渲染出现错误是什么问题_AE渲染输出损坏怎么解决?教你如何渲染才是正确的...
- win7计算机扫描仪,win7系统怎么用打印机扫描仪功能|win7系统扫描仪功能的使用方法...
- UML之Astah的基本使用教程-1
- 项目1 :家庭记账系统
- 联想开机壁纸存放位置
- 最简单的php爬虫库 QueryList