ABP Framework
ABP Framework
模板运行
安装并下载模板
安装ABP CLI,第一步是安装ABP CLI
dotnet tool install -g Volo.Abp.Cli
然后使用 abp new
命令在空文件夹中创建新解决方案:
// 项目名称为AbpBlazor.BookStore
// UI模板使用BlazorServer模式,默认为MVC
// 数据库使用MySql(EF framework)
// 添加了移动端(react-native)
abp new AbpBlazor.BookStore -u blazor-server -dbms MySQL -m react-native -csf
修改数据库
AbpBlazor.BookStore.DbMigrator和AbpBlazor.BookStore.Blazor中的appsettings.json
将数据库名称和数据库密码修改正确
初始化数据库
由于Abp使用了EF Framework,可直接通过code first进行数据库生成。
选择AbpBlazor.BookStore.DbMigrator项目并设置为启动项目
点击F5或者Ctrl+F5进行数据库的更新,更新完毕系统会自动推出运行。
运行ABP模板
将AbpBlazor.BookStore.Blazor设置为启动项,然后运行。
运行成功后浏览器会出现下图页面:
模板各项目
分层图
各层依赖关系
.Domain.Shared 项目
项目包含常量,枚举和其他对象,这些对象实际上是领域层的一部分,但是解决方案中所有的层/项目中都会使用到.
例如 BookType
枚举和 BookConsts
类 (可能是 Book
实体用到的常数字段,像MaxNameLength
)都适合放在这个项目中.
- 该项目不依赖解决方案中的其他项目. 其他项目直接或间接依赖该项目
.Domain 项目
解决方案的领域层. 它主要包含 实体, 集合根, 领域服务, 值类型, 仓储接口 和解决方案的其他领域对象.
例如 Book
实体和 IBookRepository
接口都适合放在这个项目中.
- 它依赖
.Domain.Shared
项目,因为项目中会用到它的一些常量,枚举和定义其他对象.
.Application.Contracts 项目
项目主要包含 应用服务 interfaces 和应用层的 数据传输对象 (DTO). 它用于分离应用层的接口和实现. 这种方式可以将接口项目做为约定包共享给客户端.
例如 IBookAppService
接口和 BookCreationDto
类都适合放在这个项目中.
- 它依赖
.Domain.Shared
因为它可能会在应用接口和DTO中使用常量,枚举和其他的共享对象.
.Application 项目
项目包含 .Application.Contracts
项目的 应用服务 接口实现.
例如 BookAppService
类适合放在这个项目中.
- 它依赖
.Application.Contracts
项目, 因为它需要实现接口与使用DTO. - 它依赖
.Domain
项目,因为它需要使用领域对象(实体,仓储接口等)执行应用程序逻辑.
.EntityFrameworkCore 项目
这是集成EF Core的项目. 它定义了 DbContext
并实现 .Domain
项目中定义的仓储接口.
- 它依赖
.Domain
项目,因为它需要引用实体和仓储接口.
只有在你使用了EF Core做为数据库提供程序时,此项目才会可用. 如果选择的是其他数据库提供程序那么项目的名称会改变
.EntityFrameworkCore.DbMigrations 项目
包含解决方案的EF Core数据库迁移. 它有独立的 DbContext
来专门管理迁移.
ABP是一个模块化的框架,理想的设计是让每个模块都有自己的 DbContext
类. 这时用于迁移的 DbContext
就会发挥作用. 它将所有的 DbContext
配置统一到单个模型中以维护单个数据库的模式. 对于更高级的场景,可以程序可以拥有多个数据库(每个数据库有一个或多个模块表)和多个迁移DbContext
(每个都维护不同的数据库模式)
需要注意,迁移 DbContext
仅用于数据库迁移,而不在运行时使用.
- 它依赖
.EntityFrameworkCore
项目,因为它重用了应用程序的DbContext
配置 .
只有在你使用了EF Core做为数据库提供程序时,此项目才会可用. 参阅Entity Framework Core迁移指南了解这个项目的详细信息.
.DbMigrator 项目
这是一个控制台应用程序,它简化了在开发和生产环境执行数据库迁移的操作.当你使用它时;
- 必要时创建数据库(没有数据库时).
- 应用未迁移的数据库迁移.
- 初始化种子数据(当你需要时).
这个项目有自己的
appsettings.json
文件. 所以如果要更改数据库连接字符串,请记得也要更改此文件.
初始化种子数据很重要,ABP具有模块化的种子数据基础设施. 种子数据的更多信息,请参阅文档.
虽然创建数据库和应用迁移似乎只对关系数据库有用,但即使你选择NoSQL数据库提供程序(如MongoDB),也会生成此项目. 这时,它会为应用程序提供必要的初始数据.
- 它依赖
.EntityFrameworkCore.DbMigrations
项目 (针对EF Core),因为它需要访问迁移文件. - 它依赖
.Application.Contracts
项目,因为它需要访问权限定义在初始化种子数据时为管理员用户赋予所有权限.
.HttpApi 项目
用于定义API控制器.
大多数情况下,你不需要手动定义API控制器,因为ABP的动态API功能会根据你的应用层自动创建API控制器. 但是,如果你需要编写API控制器,那么它是最合适的地方.
- 它依赖
.Application.Contracts
项目,因为它需要注入应用服务接口.
.HttpApi.Client 项目
定义C#客户端代理使用解决方案的HTTP API项目. 可以将上编辑共享给第三方客户端,使其轻松的在DotNet应用程序中使用你的HTTP API(其他类型的应用程序可以手动或使用其平台的工具来使用你的API).
ABP有动态 C# API 客户端功能,所以大多数情况下你不需要手动的创建C#客户端代理.
.HttpApi.Client.ConsoleTestApp
项目是一个用于演示客户端代理用法的控制台应用程序.
- 它依赖
.Application.Contracts
项目,因为它需要使用应用服务接口和DTO.
如果你不需要为API创建动态C#客户端代理,可以删除此项目和依赖项
.Web 项目
包含应用程序的用户界面(UI).如果使用ASP.NET Core MVC UI, 它包括Razor页面,javascript文件,样式文件,图片等...
包含应用程序主要的 appsettings.json
配置文件,用于配置数据库连接字符串和应用程序的其他配置
- 依赖
.HttpApi
项目,因为UI层需要使用解决方案的API和应用服务接口.
如果查看
.Web.csproj
源码, 你会看到对.Application
和.EntityFrameworkCore.DbMigrations
项目的引用.在编写UI层时实际上不需要这些引用. 因为UI层通常不依赖于EF Core或应用层的实现. 这个启动模板已经为分层部署做好了准备,API层托管在不同与UI层的服务器中.
但是如果你不选择
--tiered
选项, .Web项目会有这些引用,以便能够将Web,Api和应用层托管在单个应用程序站点.你可以在表示层中使用领域实体和仓储,但是根据DDD的理论,这被认为是一种不好的做法.
Test 项目
解决方案有多个测试项目,每一层都会有一个:
.Domain.Tests
用于测试领域层..Application.Tests
用于测试应用层..EntityFrameworkCore.Tests
用于测试EF Core配置与自定义仓储..Web.Tests
用于测试UI(适用于ASP.NET Core MVC UI)..TestBase
所有测试项目的基础(共享)项目.
此外, .HttpApi.Client.ConsoleTestApp
是一个控制台应用程序(不是自动化测试项目),它用于演示.Net应用程序中HTTP API的用法.
测试项目是用于做集成测试的:
- 它完全集成到ABP框架和应用程序的所有服务.
- 如果数据库提供程序是EF Core,测试项目会使用SQLite内存数据库,如果是MongoDB,它使用Mongo2Go库.
- 授权被禁用,任何的应用服务都可以在测试中轻松调用.
你依然可以编写单元测试,只不过它很难写(因为你需要准备mock/fake对象),但它的运行速度更快(因为只测试单个类并跳过所有初始化过程).
ABP功能添加流程
创建服务端代码
服务端实体部分(M层)
服务端主要包括了实体类、枚举类、实体与数据库的映射、迁移数据库、添加模块初始数据、更新数据库
创建实体
在***.Domain中创建一个模块命名的文件夹(命名空间),并创建该模块的实体类
该实体类继承于AuditedAggregateRoot
说明:
- ABP提供了两个基类:
AggregateRoot
和Entity
,其中Aggregate Root是默认驱动设计,可以直接查询和处理根实体,而AuditedAggregateRoot是在Root的基础上添加了审计属性(创建时间,创建人,最后更新时间等) - Guid是主键类型
创建枚举
在***.Domain.Shared中创建一个模块命名的文件夹(命名空间),并创建该模块的枚举类(如果没有则不用)
实体与程序建立关系
通过EFCore 将程序需实体建立关系
在***.EntityFrameworkCore项目中的.DbContext类中添加DbSet属性
public class ***StoreDbContext : AbpDbContext<BookStoreDbContext>
{
//添加的实体类
public DbSet<实体> 实体s { get; set; }
}
实体与数据库建立关系
在***.EntityFrameworkCore项目中的.DbContext类的OnModelCreating方法中添加实体的映射代码
protected override void OnModelCreating(ModelBuilder builder)
{
//创建一个人实体类
builder.Entity<实体>(b =>
{
//命名规范,其中***StoreConsts为用于表的架构和表前缀的常量值,为了统一
b.ToTable(***StoreConsts.DbTablePrefix + "Books",
***StoreConsts.DbSchema);
// 配置/映射集成属性
b.ConfigureByConvention();
b.Property(x => x.Name).IsRequired().HasMaxLength(128);
});
}
添加数据迁移类
执行命令前需生成解决方案来确保实体类被调用
在***.EntityFrameworkCore项目下通过命令行窗口运行一下命令
// 用于创建实体类的表---code first模式
dotnet ef migrations add Created_实体类_Entity
成功后会在***.EntityFrameworkCore的Migrations中新增一个类
向表里添加数据
*.Domain
项目下创建 IDataSeedContributor
的派生类
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
namespace 命名空间
{
public class ***StoreDataSeederContributor
: IDataSeedContributor, ITransientDependency
{
private readonly IRepository<实体, Guid> _repository;
public BookStoreDataSeederContributor(IRepository<实体, Guid> repository)
{
_repository = repository;
}
public async Task SeedAsync(DataSeedContext context)
{
//如果该数据库中没有数据,则创建一条
if (await _repository.GetCountAsync() <= 0)
{
// 插入一条数据
await _repository.InsertAsync(
new 实体
{
属性1 = "属性1",
属性2 = 枚举类.Dystopia
},
autoSave: true
);
}
}
}
}
更新数据库
运行***.DbMigrator
应用程序部分的创建(VM层)
主要用于创建DTO和应用服务的实现
创建DTO
在***Application.Contracts
项目中创建 实体文件夹(命名空间), 并在其中添加名为 实体
的DTO类,基类为AuditedEntityDto<Guid>
。
说明:
- DOT是在表示层和应用层之间传递数据
- AuditedEntityDto中也有审计属性
AutoMapper映射
该功能的作用是让实体和DTO类相互自动转换,在***.Application项目的.ApplicationAutoMapperProfile类中定义映射
public class ***ApplicationAutoMapperProfile : Profile
{
public ***ApplicationAutoMapperProfile()
{
// 此DTO暂定用于列表显示
CreateMap<实体, 实体Dto>();
}
}
所有实体和DTO映射都要定义
例如创建一个用于传递创建/编辑的DTO
public class CreateUpdateBookDto
{
//必填
[Required]
//长度最大128
[StringLength(128)]
public string Name { get; set; }
[Required]
// 枚举类型
public ***Type Type { get; set; } = ***Type.Undefined;
[Required]
// 事件类型
[DataType(DataType.Date)]
public DateTime PublishDate { get; set; } = DateTime.Now;
[Required]
public float Price { get; set; }
}
不要忘记在***.ApplicationAutoMapperProfile类中增加映射。
创建应用服务接口
在***.Application.Contracts项目中创建模块的命名空间,然后添加:I实体AppService类
public interface I实体AppService :
ICrudAppService< //Defines CRUD methods
实体Dto, //用于展示
Guid, //主键
PagedAndSortedResultRequestDto, //用于分页和排序
CreateUpdate实体Dto> //用于添加和修改
{}
- 框架定义应用程序服务的接口不是必需的. 但是,它被建议作为最佳实践.
ICrudAppService
定义了常见的CRUD方法:GetAsync
,GetListAsync
,CreateAsync
,UpdateAsync
和DeleteAsync
. 从这个接口扩展不是必需的,你可以从空的IApplicationService
接口继承并手动定义自己的方法(将在下一部分中完成).ICrudAppService
有一些变体, 你可以在每个方法中使用单独的DTO(例如使用不同的DTO进行创建和更新).
应用接口的实现
在***.Application项目中创建模块文件夹,并添加接口的实现
public class 实体AppService :
CrudAppService<
实体类,
实体Dto, //用于展示
Guid,
PagedAndSortedResultRequestDto,
CreateUpdateBookDto>,
I实体AppService //实现接口
{
public 实体AppService(IRepository<实体, Guid> repository)
: base(repository)
{
}
}
添加页面
本地化
在.Domain.Shared项目的
Localization下有本地换配置文件,前台部分的变量都需要在该处进行设置---页面中L后面的变量
创建页面
在.Blazor
项目下的Pages
文件夹中创建模块,并创建一个razor组件,命名为“实体.razor”。
添加主菜单
Blazor
项目中的***MenuContributor
类,在 ConfigureMainMenuAsync
方法的底部添加如下代码:
context.Menu.AddItem(
new ApplicationMenuItem(
"创建菜单",
l["Menu:创建菜单"],//菜单名称
icon: "fa fa-book"
).AddItem(
new ApplicationMenuItem(
"左侧菜单",
l[""],//名称
url: "/实体" //url
)
)
);
添加列表
在创建的“实体.razor”中,修改代码
@page "/实体" @*实体页面引用*@
@using Volo.Abp.Application.Dtos @*基类引用*@
@using Acme.BookStore.实体 @*实体类(***.Domain项目中的model)*@
@using Acme.BookStore.Localization @*本地化的基础引用*@
@using Microsoft.Extensions.Localization @*本地化的基础引用*@
@inject IStringLocalizer<BookStoreResource> L @*依赖注入,作为对象L的声明,可以使用本地化,*.Domain.Shared项目中Localization下的资源*@
@inherits AbpCrudPageBase<I实体AppService, 实体Dto, Guid, PagedAndSortedResultRequestDto, CreateUpdate实体Dto>
@*内部使用的相关类*@
<Card>
<CardHeader>
@*标题,后期会加入新建按钮,资源名称在Domain.Shared项目中Localization中*@
<h2>@L["资源名称"]</h2>
</CardHeader>
<CardBody> @*卡片的主体部分 DataGrid标签,所需实体类为展示的’实体Dto‘,数据类型为Entities,向后台发送OnDataGridReadAsync读取数据, TotalCount和PaeSize为AbpCrudPageBase中的预定义数据*@
<DataGrid TItem="实体Dto"
Data="Entities"
ReadData="OnDataGridReadAsync"
TotalItems="TotalCount"
ShowPager="true"
PageSize="PageSize">
<DataGridColumns>
@* L["Name"]如果资源中没有Name,则会显示Name;使用DisplayTemplate标签来限定显示的内容模板,例如枚举类型,时间显示方式*@
<DataGridColumn TItem="实体Dto"
Field="@nameof(实体Dto.Name)"
Caption="@L["Name"]"></DataGridColumn>
<DataGridColumn TItem="实体Dto"
Field="@nameof(实体Dto.Type)"
Caption="@L["Type"]">
<DisplayTemplate>
@L[$"Enum:实体Type.{Enum.GetName(context.Type)}"]
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="实体Dto"
Field="@nameof(实体Dto.PublishDate)"
Caption="@L["PublishDate"]">
<DisplayTemplate>
@context.PublishDate.ToShortDateString()
</DisplayTemplate>
</DataGridColumn>
<DataGridColumn TItem="实体Dto"
Field="@nameof(实体Dto.Price)"
Caption="@L["Price"]">
</DataGridColumn>
<DataGridColumn TItem="实体Dto"
Field="@nameof(实体Dto.CreationTime)"
Caption="@L["CreationTime"]">
<DisplayTemplate>
@context.CreationTime.ToLongDateString()
</DisplayTemplate>
</DataGridColumn>
</DataGridColumns>
</DataGrid>
</CardBody>
</Card>
在页面中增加-增删改
实体创建的实现
-
页面上新增“New按钮”
在“实体.razor”中修改
标签 <CardHeader> @* 创建一个按钮,并添加OpenCreateModalAsync事件 *@ <Row Class="justify-content-between"> <Column ColumnSize="ColumnSize.IsAuto"> <h2>@L["实体名称"]</h2> </Column> <Column ColumnSize="ColumnSize.IsAuto"> <Button Color="Color.Primary" Clicked="OpenCreateModalAsync">@L["New"]</Button> </Column> </Row> </CardHeader>
-
创建新建的弹框
@* 与后台的CreateModal变量关联(Modal类) *@ <Modal @ref="@CreateModal"> <ModalBackdrop /> @* 是否剧中 *@ <ModalContent IsCentered="true"> <Form> <ModalHeader> @* 弹框名称 *@ <ModalTitle>@L["New"]</ModalTitle> @* 触发的事件,取消新增 *@ <CloseButton Clicked="CloseCreateModalAsync"/> </ModalHeader> <ModalBody> <Validations @ref="@CreateValidationsRef" Model="@NewEntity" ValidateOnLoad="false"> <Validation MessageLocalizer="@LH.Localize"> @* 添加名称 *@ <Field> <FieldLabel>@L["Name"]</FieldLabel> @* 输入框绑定的是NewEntity实体类的Name属性 *@ <TextEdit @bind-Text="@NewEntity.Name"> @* 不能为空 *@ <Feedback> <ValidationError/> </Feedback> </TextEdit> </Field> </Validation> @* 枚举类型 *@ <Field> <FieldLabel>@L["Type"]</FieldLabel> <Select TValue="实体枚举类(对应的枚举类)" @bind-SelectedValue="@NewEntity.Type"> @foreach (int typeValue in Enum.GetValues(typeof(实体枚举类))) { <SelectItem TValue="实体枚举类" Value="@((实体枚举类) typeValue)"> @L[$"Enum:实体枚举类.{Enum.GetName((实体枚举类)typeValue)}"] </SelectItem> } </Select> </Field> @* 时间选择 *@ <Field> <FieldLabel>@L["PublishDate"]</FieldLabel> <DateEdit TValue="DateTime" @bind-Date="NewEntity.PublishDate"/> </Field> @* 只能输入数字和小数 *@ <Field> <FieldLabel>@L["Price"]</FieldLabel> <NumericEdit TValue="float" @bind-Value="NewEntity.Price"/> </Field> </Validations> </ModalBody> <ModalFooter> <Button Color="Color.Secondary" Clicked="CloseCreateModalAsync">@L["Cancel"]</Button> @* 保存按钮 *@ <Button Color="Color.Primary" Type="@ButtonType.Submit" PreventDefaultOnSubmit="true" Clicked="CreateEntityAsync">@L["Save"]</Button> </ModalFooter> </Form> </ModalContent> </Modal>
-
后台代码
AbpCrudPageBase基类已全部实现
更新功能的代码
-
新增编辑按钮
在每一列添加下拉菜单,下拉中展示Edit按钮。
在
DataGridColumns
中添加DataGridEntityActionsColumn
标签,第一个DataGridColumn
标签上方添加:@* 按钮触发OpenEditModalAsync方法 *@ <DataGridEntityActionsColumn TItem="实体Dto" @ref="@EntityActionsColumn"> <DisplayTemplate> <EntityActions TItem="实体to" EntityActionsColumn="@EntityActionsColumn"> <EntityAction TItem="实体Dto" Text="@L["Edit"]" Clicked="() => OpenEditModalAsync(context)" /> </EntityActions> </DisplayTemplate> </DataGridEntityActionsColumn>
-
增加编辑对话框
<Modal @ref="@EditModal"> <ModalBackdrop /> <ModalContent IsCentered="true"> <Form> <ModalHeader> <ModalTitle>@EditingEntity.Name</ModalTitle> <CloseButton Clicked="CloseEditModalAsync"/> </ModalHeader> <ModalBody> <Validations @ref="@EditValidationsRef" Model="@NewEntity" ValidateOnLoad="false"> <Validation MessageLocalizer="@LH.Localize"> <Field> <FieldLabel>@L["Name"]</FieldLabel> <TextEdit @bind-Text="@EditingEntity.Name"> <Feedback> <ValidationError/> </Feedback> </TextEdit> </Field> </Validation> <Field> <FieldLabel>@L["Type"]</FieldLabel> <Select TValue="实体Type" @bind-SelectedValue="@EditingEntity.Type"> @foreach (int typeValue in Enum.GetValues(typeof(实体Type))) { <SelectItem TValue="实体Type" Value="@((实体Type) typeValue)"> @L[$"Enum:实体Type.{Enum.GetName((实体Type)typeValue)}"] </SelectItem> } </Select> </Field> <Field> <FieldLabel>@L["PublishDate"]</FieldLabel> <DateEdit TValue="DateTime" @bind-Date="EditingEntity.PublishDate"/> </Field> <Field> <FieldLabel>@L["Price"]</FieldLabel> <NumericEdit TValue="float" @bind-Value="EditingEntity.Price"/> </Field> </Validations> </ModalBody> <ModalFooter> <Button Color="Color.Secondary" Clicked="CloseEditModalAsync">@L["Cancel"]</Button> <Button Color="Color.Primary" Type="@ButtonType.Submit" PreventDefaultOnSubmit="true" Clicked="UpdateEntityAsync">@L["Save"]</Button> </ModalFooter> </Form> </ModalContent> </Modal>
-
定义
CreateUpdate实体Dto类
映射在AutoMapper中添加
实体Dto
和CreateUpdate实体Dto类
的映射.Blazor
项目中的BookStoreBlazorAutoMapperProfile
, 替换成以下内容:namespace ***.Blazor { public class ***BlazorAutoMapperProfile : Profile { public ***BlazorAutoMapperProfile() { CreateMap<实体Dto, CreateUpdate实体Dto>(); } } }
-
使用内部自带的后台方法
删除功能的代码
-
添加删除按钮
在编辑的下面新增一个
EntityActions
标签<EntityAction TItem="实体Dto" Text="@L["Delete"]" Clicked="() => DeleteEntityAsync(context)" ConfirmationMessage="() => GetDeleteConfirmationMessage(context)" />
DeleteEntityAsync
定义在基类中. 通过向服务器发起请求删除实体.ConfirmationMessage
执行操作前显示确认消息的回调函数.GetDeleteConfirmationMessage
定义在基类中. 你可以覆写这个方法 (或传递其它值给ConfirmationMessage
参数) 以定制本地化消息.
授权部分
定义权限
设置好定义的权限,默认为使用实体名称作为权限名,他们通常是一个常量
打开***.Application.Contracts
项目中的 ***Permissions
类 (位于 Permissions
文件夹) 并替换为以下代码:
namespace ***.Permissions
{
public static class ***Permissions
{
public const string GroupName = "一般为默认,可修改";
//创建了四个常量,分别是显示,新建,修改和删除
public static class 实体
{
public const string Default = GroupName + ".实体";
public const string Create = Default + ".Create";
public const string Edit = Default + ".Edit";
public const string Delete = Default + ".Delete";
}
}
}
定义权限
打开***.Application.Contracts
项目中的 ***PermissionDefinitionProvider类 (位于
Permissions` 文件夹) 并替换为以下代码:
using ***..Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
namespace ***.Permissions
{
// 将设置的权限常量进行权限定义
public class ***PermissionDefinitionProvider : PermissionDefinitionProvider
{
// 声明权限
public override void Define(IPermissionDefinitionContext context)
{
// 添加一个权限组
var group = context.AddGroup(***Permissions.GroupName, L("Permission:实体"));
// 定义实体的权限,该权限为父权限
var permission = group.AddPermission(***Permissions.实体.Default, L("Permission:实体"));
// 添加子权限内容
permission.AddChild(***Permissions.实体.Create, L("Permission:实体.Create"));
permission.AddChild(***Permissions.实体.Edit, L("Permission:实体.Edit"));
permission.AddChild(***Permissions.实体.Delete, L("Permission:实体.Delete"));
}
private static LocalizableString L(string name)
{
// 创建本地化资源
return LocalizableString.Create<***Resource>(name);
}
}
}
本地换权限名称
上述代码中Permission:实体
等所有Permission:
的都需要在本地化中创建相关的显示信息,新增内容如下:
"Permission:权限组": "可自定义内容",
"Permission:实体`": "可自定义内容",
"Permission:实体`.Create": "Creating...",
"Permission:实体`.Edit": "Editing..",
"Permission:实体`.Delete": "Deleting.."
授权
打开BookAppService
类, 设置策略名称为上面定义的权限名称.
using System;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;
namespace Acme.BookStore.Books
{
public class ***AppService :
CrudAppService<
实体,
实体Dto,
Guid,
PagedAndSortedResultRequestDto,
CreateUpdate实体Dto>,
I实体AppService
{
public 实体AppService(IRepository<实体, Guid> repository)
: base(repository)
{
GetPolicyName = ***Permissions.实体.Default;
GetListPolicyName = ***Permissions.实体.Default;
CreatePolicyName = ***Permissions.实体.Create;
UpdatePolicyName = ***Permissions.实体.Edit;
DeletePolicyName = ***Permissions.实体.Delete;
}
}
}
权限授权页面
使用admin登录后,可在*管理 -> Identity -> 角色* 页面
中设置相关权限
添加集成测试
在***.Application.Tests
项目中添加一个 实体AppService_Tests
类
using System;
using System.Linq;
using System.Threading.Tasks;
using Shouldly;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Validation;
using Xunit;
namespace 命名空间
{
public class 实体AppService_Tests : ***ApplicationTestBase
{
private readonly I实体AppService _appService;
public 实体AppService_Tests()
{
// 注入service
_appService = GetRequiredService<I实体AppService>();
}
[Fact]
public async Task Should_Get_List()
{
// 获得列表
var result = await _appService.GetListAsync(
new PagedAndSortedResultRequestDto()
);
// 通过注解判断是否正确
result.TotalCount.ShouldBeGreaterThan(0);
result.Items.ShouldContain(b => b.Name == "名字");
}
}
}