自从 .Net Core 推出以后,对 .Net 开发者来说可以说是一次新生,但是当前 .Net Core 的相关轮子也确实比较少。很多人都在不断的学习,不断的创造新的轮子,以使 .Net Core 可以更好的使用。
从整体架构的角度上看,感觉还是 ABP VNext 的设计更好,考虑的更全面一点,而且是面向微服务的,对未来的扩展也比较方便。本文的主要目的就是介绍 ABP VNext 的项目的创建及简单项目搭建,即一个后端的 webapi 项目。虽然很多国内很多大牛也在不断的搭建 .Net Core 的项目,但是总体上来看,都有参照 ABP VNext 设计的地方。所以感觉写这个样一篇文章就更有必要了,也希望能为降低 ABP VNext 的使用门槛有所帮助。
以下内容比较多,为了不造成阅读困难,可考虑喜欢的部分进行阅读。
对于项目的创建,其实很简单的官网上有详细的说明。这里不过多的介绍。
我想介绍的是一个由界面的代码生成器,这个也是国内EasyAbp社区提供的,目的也是为了降低 ABP VNext 的使用门槛。这个项目就是AbpHelper.GUI,大家可以下载源码查看具体的实现,并可以定制生成安装包;也可以直接下载社区提供好的安装包。
通过上面界面展示,大家可以看到,其不仅提供了项目的创建,同时也提供业务代码的生成功能,同时还提供必须环境(Abp.Cli
和 AbpHelper.Cli
)的安装功能。
说明:请确保您的电脑上安装了 dotnet 的 cli。
对于 ABP VNext 项目的创建,目前此软件提供了 application 和 module 的创建,console 暂时还没有实现。由于我目前的项目基本都是SPA形式的。所以代码生成上我只是用了CRUD。有兴趣请自行研究,比如我就对对其进行了简单修改并重新打包使用。
写本文时 AbpHelper.GUI 的版本是 0.5.0
上面截图就是 application
项目的创建,让我们来看看点击 Execute
后的效果。
我们先来看看通过 AbpHelper.GUI 创建的 module 项目的最终样子。
通过上图可以看出, module 类应该主要是针对微服务准备的,所以内置了 docker 相关的文件,而且项目模块感觉也挺多的(至少和 application 项目比是这样)。因为这块暂时我是用不到的,所以这里只是给看看搭建生成的效果,不会过多的展开。
从上面的 项目创建 部分中可以看到,虽然创建的是没有界面的项目,但是这个“没有界面”只是针对的pc端,而移动端是不包括在内容的。所以最终生成的项目中有个react-native的移动端项目,个人觉得可以直接删除。
进入 aspnet-core 文件夹后,个人觉得 Hbw.Test.sln.DotSettings 亦可以直接删除。
总体结构
由于是WebApi
项目,所以这个项目中可以考虑将wwwroot
和所有js
文件全部删除。其次,package.json
文件也可以删除。最后把此项目设置为启动项目。
appsetting.json
修改当前的数据库连接,这个也是可以在创建的时候就定义好的。
XXXHttpApiHostModule.cs
DependsOn
部分的AbpAspNetCoreMvcUiBasicThemeModule
和AbpAccountWebIdentityServerModule
ConfigureServices
方法中的ConfigureLocalization();
(本地化个人不需要有,但是如果你是做的国际项目就需要了)ConfigureAuthentication
方法里的内容全部注释,后期考虑使用jwt
验证,而不是默认的IdentityServer
OnApplicationInitialization
方法中的app.UseAbpRequestLocalization();
、app.UseAuthentication();
、app.UseJwtTokenMiddleware();
、app.UseAuthorization();
和app.UseIdentityServer();
Program.cs
在日志定义部分添加对控制台的输出和是日志文件按天生成日志文件的代码。具体如下:
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt",rollingInterval: RollingInterval.Day))
.WriteTo.Console()
.CreateLogger();
修改后可以在控制台查看实时输出的日志信息,方便开发期间的代码调试。
引入包删除
删除所有包含IdentityServer
的依赖包。
注意:完成上述的调整后,需要编译项目并对出现的错误进行调整,比如删除
using
中的多余库等。
只需要删除包含Account
、PermissionManagement
和 FeatureManagement
的依赖包,并删除多余的引用即可。同时也可删除 *.HttpApi 项目下的 Models
文件夹。
删除包含Account
、PermissionManagement
、FeatureManagement
、IdentityServer
和BackgroundJobs
的依赖包,并删除多余的引用。
注释掉 *.EntityFrameworkCore.DbMigrations
下的xxMigrationsDbContext.cs
类中的OnModelCreating
方法下的builder.ConfigurePermissionManagement();
、FeatureManagement
、builder.ConfigureBackgroundJobs();
和builder.ConfigureIdentityServer();
。删除多余的引用。
修改 *.DbMigrator
下的 appsetting.json
中的数据库连接,同时修改日志文件为每天生成一个文件。(具体可参照Program.cs中的方式)
删除包含Account
、PermissionManagement
、FeatureManagement
、IdentityServer
和BackgroundJobs
的依赖包。
删除Domin
下面的IdentityServer
文件夹。
注释掉测试类库TestBase
下的报错代码和 using
引用(如果整个test
文件夹已经删除,则跳过此步骤)。
注释到DbMigrator
库下的报错代码,可能需要安装Microsoft.Extensions.Hosting
nuget
包(如果需要)。
删除包含Account
、FeatureManagement
和PermissionManagement
的依赖包,并删除多余的引用。
删除Application.Contracts
库下的Permissions
文件夹。
注意:在调整完成并且编译不报错的情况下,在执行*.DbMigrator 项目之前,一定要删除*.EntityFrameworkCore.DbMigrations 中默认生成的Migrations文件夹中的文件,并重新生成新的迁移文件。
以上调整完成,运行没有报错的情况下,可以看到以下默认的 webapi
。
从截图中可以看出,有些默认的 webapi
是不需要的,可以考虑不显示这些 webapi
。只需要调整 AddSwaggerGen
方法中的代码即可,如下:
options.SwaggerDoc("v1", new OpenApiInfo {Title = "Test API", Version = "v1"});
options.DocInclusionPredicate((docName, description) =>
{
if (description.GroupName!=null && (description.GroupName.StartsWith("Identity") ||
description.GroupName.StartsWith("Abp") || description.GroupName.StartsWith("Features") ||
description.GroupName == "Profile") || description.GroupName.StartsWith("Tenant"))
{
return false;
}
else
{
return true;
}
});
Serilog
ABP VNext 默认使用了 Serilog 进行日志的记录,并且默认的初始化工作也基本完成了。当然你也可以对现有的配置进行调整。示例代码如下:
Log.Logger = new LoggerConfiguration()
#if DEBUG
.MinimumLevel.Debug()
#else
.MinimumLevel.Information()
#endif
.MinimumLevel.Override("Microsoft", LogEventLevelInformation)
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.log",rollingInterval:RollingInterval.Day, outputTemplate: "{Timestamp:HH:mm} ||{Level} || {SourceContext:l} || {Message} || {Exception} |end {NewLine}"))
.WriteTo.Console()
.CreateLogger();
对于想要详细了解 serilog 配置的朋友,可以参考官网信息。
具体的使用也十分简单,具体步骤如下:
1、只需要在具体的类中定义 ILogger
2、在其类的构造函数中初始化上面定义的对象。
3、在需要的地方使用。
示例:
public class Test
{
public ILogger<Test> Logger { get; set; }
public Test()
{
Logger = NullLogger<Test>.Instance;
}
public void Method1()
{
Logger.LogInformation("Started Method1...");
}
}
LogDashboard
LogDashboard 是一个 .Net Core 的开源项目,地址为:https://github.com/realLiangshiwei/LogDashboard。这是一个可通过面板管理日志信息的开源项目,通过它可以方便的查看日志的类别、个数等信息。
使用方式及步骤
1、通过 Nuget
包管理器安装 LogDashboard
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yKDBEjrM-1597479486461)(./logdash.png)]
2、在ConfigureServices
方法最后添加如下代码
context.Services.AddLogDashboard(opt => opt.SetRootPath(hostingEnvironment.ContentRootPath));
3、在OnApplicationInitialization
方法最后添加以下代码
app.UseLogDashboard();
注意:配置的日志文件后缀必须改成
.log
形式,否则无法读取到日志信息进行显示
4、然后启动项目,访问路径为:/logdashboard
,效果如下
关于日志权限等可参考官网了解更多。
关于此部分的代码分析,可参考此网站的进行详细的了解。
使用上,因为在数据库(EF
)层和领域层保留了 Volo.Abp.AuditLogging.EntityFrameworkCore
和 Volo.Abp.AuditLogging.Domain
的引用,模块中的依赖也没有注释,所以只要创建了实体类并继承了 AggregateRoot
对于 Entity
没有进行尝试,但是总体上来说,审计日志的实现很方便。具体也看参考官网说明文档。
要想使用此功能,必须使用 *.DbMigrator
项目来进行种子数据的初始化保存。关于此部分的详细说明可参考官网说明。
具体使用中,必须为相应需要创建种子数据的实体类单独创建其提供者类,此类必须继承自IDataSeedContributor
。具体可参考官网的示例,如下:
using System;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Guids;
namespace Acme.BookStore
{
public class BookStoreDataSeedContributor
: IDataSeedContributor, ITransientDependency
{
private readonly IRepository<Book, Guid> _bookRepository;
private readonly IGuidGenerator _guidGenerator;
public BookStoreDataSeedContributor(
IRepository<Book, Guid> bookRepository,
IGuidGenerator guidGenerator)
{
_bookRepository = bookRepository;
_guidGenerator = guidGenerator;
}
public async Task SeedAsync(DataSeedContext context)
{
if (await _bookRepository.GetCountAsync() > 0)
{
return;
}
var book = new Book(
id: _guidGenerator.Create(),
name: "The Hitchhiker's Guide to the Galaxy",
type: BookType.ScienceFiction,
publishDate: new DateTime(1979, 10, 12),
price: 42
);
await _bookRepository.InsertAsync(book);
}
}
}
关于这块的内容,官网中没有具体的进行介绍,也没有具体的使用 demo
,但是源码中有这块内容的定义,如下:
而从 ASP .NET Boilerplate Project 中的简单介绍可以看出,它是配合多租户使用的。似乎和设置(Settings)、配置(Configuration)的作用差不多,只是因为“业务”上的作用不同而单独拿出来了。
当然你也可以查看github上的中文翻译。
配置系统 是在启动时配置应用程序很好的方式。 除了配置之外, ABP
提供了另外一种设置和获取应用程序设置的方式。
设置存储在动态数据源(通常是数据库)中的键值对。 设置系统预构建了用户、租户、全局和默认设置方法并且可以进行扩展。
在创建系统后,默认在 *.Domain
层中添加了 Settings
文件夹,里面预定义好了设置需要的类,直接在里面进行定义就可以了。具体的使用可以参考官网文档说明。
预定义的设置类,在运行数据迁移时会被执行创建,应该新建一个类完成数据库的保存,即像种子数据一样在数据迁移时完成数据库中表数据的初始化。
一个简单的示例如下:
// 保存设置到数据中,作为种子数据在数据迁移时完成
public class TestSettingDataSeedContributor : IDataSeedContributor, ITransientDependency
{
private readonly IRepository<Setting, Guid> _settingsRepository;
private readonly IGuidGenerator _guidGenerator;
private readonly ISettingProvider _settingProvider;
// 日志的使用方式
public ILogger<TestSettingDataSeedContributor> Logger { get; set; }
public TestSettingDataSeedContributor(IRepository<Setting,Guid> settingsRepository,
IGuidGenerator guidGenerator,
ISettingProvider settingProvider)
{
this._settingsRepository = settingsRepository;
this._guidGenerator = guidGenerator;
this._settingProvider = settingProvider;
Logger = NullLogger<TestSettingDataSeedContributor>.Instance;
}
public async Task SeedAsync(DataSeedContext context)
{
Logger.LogInformation("开始执行setting的种子数据");
await this._settingsRepository.InsertAsync(new Setting(
this._guidGenerator.Create(),
TestSettings.MySetting1,
await this._settingProvider.GetOrNullAsync(TestSettings.MySetting1),
GlobalSettingValueProvider.ProviderName));
}
}
在对应的Identity
中定义了默认展示的webapi
,其中用户等的api
还包含了验证授权等功能,可以在源码的/modules/identity
路径下查看具体实现过程。所以个人觉得,如果用户、角色、权限等模块不是使用的系统提供的实现,或者对这块不是很熟悉的话,建议禁止显示这些api
。(在上面的简单优化项目部分说明了如何实现)
令人郁闷的是,如果删除了所有包含 Identity 的依赖包,理论上所有的 webapi
接口应该是不显示的,但是显示很骨干——报错了,暂时也不知道怎么修改。而即使能够修改到不报错,个人觉得代码调整过多,利用其框架生成基本框架代码的目的似乎就没那么多的优势了。
虚拟文件系统使得管理物理上不存在于文件系统中(磁盘)的文件成为可能. 它主要用于将(js, css, image...
)文件嵌入到程序集中, 并在运行时将它们象物理文件一样使用。
在初始创建好的项目中,虚拟文件主要是为了完成本地化(多语言)处理的。其主要资源放在 *.Domain.Shared 库中,并在 Startup 中通过此句代码 app.UseVirtualFiles() 使用。具体可参考官网说明文档。
如果是在 MVC
的项目中,同时也可以对 wwwroot
文件夹中的静态资源进行虚拟化的,但是对于只是开发 webapi
项目的话,也可以删除与此相关代码。
如果需要删除的话,可通过下面步骤完成。
删除 *.Domain.Shared 库中依赖包 Microsoft.Extensions.FileProviders.Embedded
和 Localization
文件夹。
注释 xxxDomainSharedModule 类下的 ConfigureServices 方法中的配置内容。
注释 *.HttpApi 层中控制器构造函数中的代码,同时注册 *.Application 层中 XXXAppService 类构造函数中的代码,注意删除不需要的 using 引用。
注释 *.HttpApi.Host 层中 XXXHttpApiHostModule.cs 中的 app.UseVirtualFiles();
语句。
维基百科:“软件多租户是指一个软件架构的实例软件运行在一个服务器上,但存在多个租户。租户是一组共享一个公共的用户访问特定权限的软件实例。多租户架构,软件应用程序旨在提供每个租户专用的实例包括数据、配置、用户管理、租户个体功能和非功能属性。多租户与多实例架构,独立的软件实例代表不同的租户”操作多租户一般用来创建SaaS(软件即服务)应用程序(云计算)。
关于这块内容,个人是决定可以放着不删除相关依赖包,当想用的时候随时可以用,就是暂时不用也不影响系统的开发。想了解更多内容,可通过阅读官方文档了解更多。
也可以参考阅读之前的ABP框架的关于多租户的文档。
ABP VNext 默认定义好的的用户表,国内估计除了做演示会用到,实际项目中用的应该是不多的(除非你对源码理解到一定的深度了,知道怎么扩展)。还是会根据具体的项目需求,重新定义用户的,就和其他的领域实体一样根据实际需要定义使用。
如果要删除与此相关内容,可通过以下步骤完成:
删除 *.Domain 层下的 Users 文件夹
删除数据库上下文中的用户数据集和相关配置代码
删除测试中相关代码(如果保留了单元测试部分)
注释 *.EntityFrameworkCore.DbMigrations 层下 XXXMigrationsDbContext.cs 类中的代码 builder.ConfigureIdentity();
(个人觉得不显示默认 webapi
的时候,这行代码就应该注释,以减少系统默认表的生成)
如果注释了
builder.ConfigureIdentity();
,那么 *.DbMigrator 库下的 MigrateAsync 方法下的await SeedDataAsync();
也应该注释,否则生成系统默认种子数据时会报错,但是不影响数据库结构的生成。
我们以 Book.cs
类为例子进行接下来的介绍。
类定义如下:
public class Book : AggregateRoot<Guid>
{
[DisplayName("书名")]
public string BookName { get; set; }
[DisplayName("作者")]
public string Author { get; set; }
[DisplayName("出版时间")]
public DateTime PublishDate { get; set; }
}
完成类的定义后不急着添加迁移文件,打开 AbpHelper GUI 并进入 CRUD 标签页,然后选中项目解决方案文件和填写好实体类名称,即 Book。如下图所示:
如上图,根据实际情况选中配置内容,在最后点击执行按钮就行代码的生成即可。
*.Domain层实体类
public class Book : AggregateRoot<Guid>
{
[DisplayName("书名")]
public string BookName { get; set; }
[DisplayName("作者")]
public string Author { get; set; }
[DisplayName("出版时间")]
public DateTime PublishDate { get; set; }
protected Book()
{
}
public Book(Guid id, string bookName, string author, DateTimepublishDate) : base(id)
{
BookName = bookName;
Author = author;
PublishDate = publishDate;
}
}
如果选中生成构造函数,则在定义的时候,可考虑给 set 添加 protected 修饰符。
*.EntityFrameworkCore 层
XXXDbContext.cs
类添加了如下代码public DbSet<Book> Books { get; set; }
XXXDbContextModelCreatingExtensions
扩展类中添加如下配置代码builder.Entity<Book>(b =>
{
b.ToTable(TestConsts.DbTablePrefix + "Books", TestConsts.DbSchema);
b.ConfigureByConvention();
/* Configure more properties here */
b.Property(x => x.Id).HasComment("主键,Guid").IsRequired();
b.Property(x => x.BookName).HasComment("书名");
b.Property(x => x.Author).HasComment("作者");
b.Property(x => x.PublishDate).HasComment("出版时间");
});
添加的注释自动生成,这也就会最终生成到数据表的说明中,即在数据库中也可知道各个表字段的注释了。
应用层的代码结构
XXXApplicationAutoMapperProfile
类中的代码如下:
public TestApplicationAutoMapperProfile()
{
/* 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<CreateBookDto, Book>(MemberList.Source);
CreateMap<UpdateBookDto, Book>(MemberList.Source);
}
以上只是简单展示了一下通过创建一个实体类,然后通过代码生成器就可以生成数据库相关,应用层相关的逻辑代码(分页列表展示,根据 id
得到一条记录,增删改查等)。
由于生成代码时勾选的配置的不同,最终生成的代码也是有区别的,有兴趣的朋友可以亲自体验一下。
而在生成了代码之后,你需要添加新迁移文件,然后执行迁移项目重新生成新的表。在这之后只需要运行程序即可。最后来看看效果图:
以上内容记录我自己研究 ABP VNext 框架和项目使用中的相关内容,希望通过此文抛砖引玉,能有更多的人分享相关示例,也希望社区可以推出更多的示例吧。