Abp VNext 项目创建简介

简介

自从 .Net Core 推出以后,对 .Net 开发者来说可以说是一次新生,但是当前 .Net Core 的相关轮子也确实比较少。很多人都在不断的学习,不断的创造新的轮子,以使 .Net Core 可以更好的使用。

从整体架构的角度上看,感觉还是 ABP VNext 的设计更好,考虑的更全面一点,而且是面向微服务的,对未来的扩展也比较方便。本文的主要目的就是介绍 ABP VNext 的项目的创建及简单项目搭建,即一个后端的 webapi 项目。虽然很多国内很多大牛也在不断的搭建 .Net Core 的项目,但是总体上来看,都有参照 ABP VNext 设计的地方。所以感觉写这个样一篇文章就更有必要了,也希望能为降低 ABP VNext 的使用门槛有所帮助。

以下内容比较多,为了不造成阅读困难,可考虑喜欢的部分进行阅读。

项目创建

对于项目的创建,其实很简单的官网上有详细的说明。这里不过多的介绍。

我想介绍的是一个由界面的代码生成器,这个也是国内EasyAbp社区提供的,目的也是为了降低 ABP VNext 的使用门槛。这个项目就是AbpHelper.GUI,大家可以下载源码查看具体的实现,并可以定制生成安装包;也可以直接下载社区提供好的安装包

有图有真相,先看图。
在这里插入图片描述
在这里插入图片描述

通过上面界面展示,大家可以看到,其不仅提供了项目的创建,同时也提供业务代码的生成功能,同时还提供必须环境(Abp.CliAbpHelper.Cli)的安装功能。

说明:请确保您的电脑上安装了 dotnetcli

对于 ABP VNext 项目的创建,目前此软件提供了 applicationmodule 的创建,console 暂时还没有实现。由于我目前的项目基本都是SPA形式的。所以代码生成上我只是用了CRUD。有兴趣请自行研究,比如我就对对其进行了简单修改并重新打包使用。

写本文时 AbpHelper.GUI 的版本是 0.5.0

上面截图就是 application 项目的创建,让我们来看看点击 Execute 后的效果。

在这里插入图片描述

关于 module 项目的简单说明

我们先来看看通过 AbpHelper.GUI 创建的 module 项目的最终样子。
在这里插入图片描述
在这里插入图片描述

通过上图可以看出, module 类应该主要是针对微服务准备的,所以内置了 docker 相关的文件,而且项目模块感觉也挺多的(至少和 application 项目比是这样)。因为这块暂时我是用不到的,所以这里只是给看看搭建生成的效果,不会过多的展开。

简单优化项目

1、移动端项目删除

从上面的 项目创建 部分中可以看到,虽然创建的是没有界面的项目,但是这个“没有界面”只是针对的pc端,而移动端是不包括在内容的。所以最终生成的项目中有个react-native的移动端项目,个人觉得可以直接删除。

2、*.sln.DotSettings

进入 aspnet-core 文件夹后,个人觉得 Hbw.Test.sln.DotSettings 亦可以直接删除。

3、*.HttpApi.Host 的调整

  • 总体结构

    由于是WebApi项目,所以这个项目中可以考虑将wwwroot和所有js文件全部删除。其次,package.json文件也可以删除。最后把此项目设置为启动项目。

  • appsetting.json

    修改当前的数据库连接,这个也是可以在创建的时候就定义好的。

  • XXXHttpApiHostModule.cs

    • 注释DependsOn部分的AbpAspNetCoreMvcUiBasicThemeModuleAbpAccountWebIdentityServerModule
    • 注释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中的多余库等。

4、*.HttpApi 和 *.HttpApi.Client 的调整

只需要删除包含AccountPermissionManagementFeatureManagement 的依赖包,并删除多余的引用即可。同时也可删除 *.HttpApi 项目下的 Models 文件夹。

5、.EntityFrameworkCore、.EntityFrameworkCore.DbMigrations 和 *.DbMigrator 的调整

  • 删除包含AccountPermissionManagementFeatureManagementIdentityServerBackgroundJobs的依赖包,并删除多余的引用。

  • 注释掉 *.EntityFrameworkCore.DbMigrations下的xxMigrationsDbContext.cs类中的OnModelCreating方法下的builder.ConfigurePermissionManagement();FeatureManagementbuilder.ConfigureBackgroundJobs();builder.ConfigureIdentityServer();。删除多余的引用。

  • 修改 *.DbMigrator 下的 appsetting.json 中的数据库连接,同时修改日志文件为每天生成一个文件。(具体可参照Program.cs中的方式)

6、*.Domain 和 *.Domain.Shared 的调整

  • 删除包含AccountPermissionManagementFeatureManagementIdentityServerBackgroundJobs的依赖包。

  • 删除Domin下面的IdentityServer文件夹。

  • 注释掉测试类库TestBase下的报错代码和 using 引用(如果整个test文件夹已经删除,则跳过此步骤)。

  • 注释到DbMigrator库下的报错代码,可能需要安装Microsoft.Extensions.Hosting nuget包(如果需要)。

7、*.Application 和 *.Application.Contracts 的调整

  • 删除包含AccountFeatureManagementPermissionManagement的依赖包,并删除多余的引用。

  • 删除Application.Contracts库下的Permissions文件夹。

注意:在调整完成并且编译不报错的情况下,在执行*.DbMigrator 项目之前,一定要删除*.EntityFrameworkCore.DbMigrations 中默认生成的Migrations文件夹中的文件,并重新生成新的迁移文件。

8、WebApi显示的调整

以上调整完成,运行没有报错的情况下,可以看到以下默认的 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;
    }
});

调整的再次运行项目,可以看到以下效果。
在这里插入图片描述

相关模块说明

1、日志相关

  • 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<T> 的对象。

    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
    在这里插入图片描述

    2、在ConfigureServices方法最后添加如下代码

    context.Services.AddLogDashboard(opt => opt.SetRootPath(hostingEnvironment.ContentRootPath));
    

    3、在OnApplicationInitialization方法最后添加以下代码

    app.UseLogDashboard();
    

    注意:配置的日志文件后缀必须改成 .log 形式,否则无法读取到日志信息进行显示

    4、然后启动项目,访问路径为:/logdashboard ,效果如下
    在这里插入图片描述

    关于日志权限等可参考官网了解更多

2、审计日志

关于此部分的代码分析,可参考此网站的进行详细的了解。

使用上,因为在数据库(EF)层和领域层保留了 Volo.Abp.AuditLogging.EntityFrameworkCoreVolo.Abp.AuditLogging.Domain 的引用,模块中的依赖也没有注释,所以只要创建了实体类并继承了 AggregateRoot<T> 类即可实现审计日志的相关记录保存了。

对于 Entity<T> 没有进行尝试,但是总体上来说,审计日志的实现很方便。具体也看参考官网说明文档

3、迁移种子数据(Data Seeding)

要想使用此功能,必须使用 *.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);
        }
    }
}

4、特征管理(Feature Management)

关于这块的内容,官网中最新的文章已经给出(基于版本3.1),我已经翻译完成并发表,也可在源码中查看这块内容的定义,如下:
在这里插入图片描述

而从 ASP .NET Boilerplate Project 中的简单介绍可以看出,它是配合多租户使用的。似乎和设置(Settings)、配置(Configuration)的作用差不多,只是因为“业务”上的作用不同而单独拿出来了。

当然你也可以查看github上的中文翻译

5、自定义参数与配置(Settings)

配置系统 是在启动时配置应用程序很好的方式。 除了配置之外, 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));
    }
}

6、现有api 的生成

在对应的Identity中定义了默认展示的webapi,其中用户等的api还包含了验证授权等功能,可以在源码的/modules/identity路径下查看具体实现过程。所以个人觉得,如果用户、角色、权限等模块不是使用的系统提供的实现,或者对这块不是很熟悉的话,建议禁止显示这些api。(在上面的简单优化项目部分说明了如何实现)

令人郁闷的是,如果删除了所有包含 Identity 的依赖包,理论上所有的 webapi 接口应该是不显示的,但是显示很骨干——报错了,暂时也不知道怎么修改。而即使能够修改到不报错,个人觉得代码调整过多,利用其框架生成基本框架代码的目的似乎就没那么多的优势了。

7、虚拟文件系统

虚拟文件系统使得管理物理上不存在于文件系统中(磁盘)的文件成为可能. 它主要用于将(js, css, image...)文件嵌入到程序集中, 并在运行时将它们象物理文件一样使用。

在初始创建好的项目中,虚拟文件主要是为了完成本地化(多语言)处理的。其主要资源放在 *.Domain.Shared 库中,并在 Startup 中通过此句代码 app.UseVirtualFiles() 使用。具体可参考官网说明文档

如果是在 MVC 的项目中,同时也可以对 wwwroot 文件夹中的静态资源进行虚拟化的,但是对于只是开发 webapi 项目的话,也可以删除与此相关代码。

如果需要删除的话,可通过下面步骤完成。

  • 删除 *.Domain.Shared 库中依赖包 Microsoft.Extensions.FileProviders.EmbeddedLocalization 文件夹。

  • 注释 xxxDomainSharedModule 类下的 ConfigureServices 方法中的配置内容。

  • 注释 *.HttpApi 层中控制器构造函数中的代码,同时注册 *.Application 层中 XXXAppService 类构造函数中的代码,注意删除不需要的 using 引用。

  • 注释 *.HttpApi.Host 层中 XXXHttpApiHostModule.cs 中的 app.UseVirtualFiles(); 语句。

8、多租户

维基百科:“软件多租户是指一个软件架构的实例软件运行在一个服务器上,但存在多个租户。租户是一组共享一个公共的用户访问特定权限的软件实例。多租户架构,软件应用程序旨在提供每个租户专用的实例包括数据、配置、用户管理、租户个体功能和非功能属性。多租户与多实例架构,独立的软件实例代表不同的租户”操作多租户一般用来创建SaaS(软件即服务)应用程序(云计算)。

关于这块内容,个人是决定可以放着不删除相关依赖包,当想用的时候随时可以用,就是暂时不用也不影响系统的开发。想了解更多内容,可通过阅读官方文档了解更多。

也可以参考阅读之前的ABP框架的关于多租户的文档。

9、现有的用户表

ABP VNext 默认定义好的的用户表,国内估计除了做演示会用到,实际项目中用的应该是不多的(除非你对源码理解到一定的深度了,知道怎么扩展)。还是会根据具体的项目需求,重新定义用户的,就和其他的领域实体一样根据实际需要定义使用。

如果要删除与此相关内容,可通过以下步骤完成:

  • 删除 *.Domain 层下的 Users 文件夹

  • 删除数据库上下文中的用户数据集和相关配置代码

  • 删除测试中相关代码(如果保留了单元测试部分)

  • 注释 *.EntityFrameworkCore.DbMigrations 层下 XXXMigrationsDbContext.cs 类中的代码 builder.ConfigureIdentity(); (个人觉得不显示默认 webapi 的时候,这行代码就应该注释,以减少系统默认表的生成)

如果注释了builder.ConfigureIdentity(); ,那么 *.DbMigrator 库下的 MigrateAsync 方法下的 await SeedDataAsync(); 也应该注释,否则生成系统默认种子数据时会报错,但是不影响数据库结构的生成。

简单使用说明

1、在 *.Domain 层中创建实体类

我们以 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。如下图所示:
在这里插入图片描述

如上图,根据实际情况选中配置内容,在最后点击执行按钮就行代码的生成即可。

2、查看具体的生成代码

  • *.Domain层实体类

    • Book 类的代码变为:
    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);
}

3、解释一下

以上只是简单展示了一下通过创建一个实体类,然后通过代码生成器就可以生成数据库相关,应用层相关的逻辑代码(分页列表展示,根据 id 得到一条记录,增删改查等)。

由于生成代码时勾选的配置的不同,最终生成的代码也是有区别的,有兴趣的朋友可以亲自体验一下。

而在生成了代码之后,你需要添加新迁移文件,然后执行迁移项目重新生成新的表。在这之后只需要运行程序即可。最后来看看效果图:

在这里插入图片描述

总结

以上内容记录我自己研究 ABP VNext 框架和项目使用中的相关内容,希望通过此文抛砖引玉,能有更多的人分享相关示例,也希望社区可以推出更多的示例吧。

©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页