当前位置: 首页 > 工具软件 > Boilerplate > 使用案例 >

从ASP.NET Boilerplate v5 +到ABP框架的迁移

景书
2023-12-01


ABP框架是开源 ASP.NET Boilerplate框架的 继承者。本指南旨在帮助 您将现有解决方案(使用 ASP.NET Boilerplate框架开发) 迁移到ABP框架。

介绍

自2013年以来ASP.NET Boilerplate一直在积极开发中。它受到社区的喜爱、使用和贡献。它最初是作为开发人员的附带项目进行的,但现在,除了强大的社区支持之外,它还由Volosoft公司正式维护和改进。

ABP框架的目标与ASP.NET Boilerplate框架相同:不要重复自己!它提供了基础结构、工具和启动模板,使开发人员在开发企业软件解决方案时更加轻松。

如果您想知道为什么我们需要重新编写ASP.NET Boilerplate框架,请参阅介绍博客文章

我应该迁移吗?

不,您不必!

  • ASP.NET Boilerplate仍在积极开发和维护中。
  • 它还可以在最新的ASP.NET Core和相关的库和工具上运行。它是最新的。

但是,如果您想利用新的ABP框架特征和新的架构机会(例如对NoSQL数据库的支持,微服务兼容性,高级模块化),您可以将该文档用作指南。

那ASP.NET Zero呢?

ASP.NET Zero是由ASP.NET Boilerplate核心团队在ASP.NET Boilerplate框架之上开发的商业产品。它提供了预构建的应用程序特征,代码生成工具和外观漂亮的现代UI。它得到了全球数千家公司的信任和使用。

我们创建了ABP商业版,以替代ASP.NET Zero。与ASP.NET Zero相比,ABP商业版具有更高的模块化和可升级性。与ASP.NET Zero相比,它目前具有较少的特征,但是随着时间的推移,差距将逐渐缩小(它还具有ASP.NET Zero中不存在的某些特征)。

我们认为,在启动新应用程序时,ASP.NET Zero仍然是一个不错的选择。它是可用于生产的成熟解决方案,可以作为完整的源代码交。它正在积极开发中,我们正在不断添加新特征。

我们建议您不要将基于ASP.NET Zero的解决方案迁移到ABP商业版,如果:

  • 您的ASP.NET Zero解决方案已经成熟,并且正在维护中,而不是快速的发展。
  • 您没有足够的开发时间来执行迁移。
  • 整体解决方案适合您的业务。
  • 您已经根据需要自定义了太多现有的ASP.NET Zero功能。

我们还建议您根据需要比较两种产品的特征。

如果您有一个基于ASP.NET Zero的解决方案,并且想要迁移到ABP商业版,则本指南也将为您提供帮助。

ASP.NET MVC 5.x项目

ABP框架不支持ASP.NET MVC 5.x,仅适用于ASP.NET Core。因此,如果您迁移基于ASP.NET MVC 5.x的项目,则还将处理.NET Core迁移。

迁移进度

我们通过获取 ASP.NET Boilerplate 框架的最佳组成部分来设计ABP框架,因此,如果您开发了基于ASP.NET Boilerplate的应用程序,则对您来说会很熟悉。

ASP.NET Boilerplate中,我们在UI方面没有做很多工作,但是使用了一些免费的主题(在另一方面,ASP.NET Zero 使用了metronic主题)。在ABP框架中,我们在UI方面做了很多工作(尤其是对于MVC / Razor Pages UI,因为Angular已经拥有了自己的良好模块化系统)。因此,迁移中最具挑战性的部分将是解决方案的用户界面

ABP框架(和ASP.NET Boilerplate)是基于领域驱动设计模式和原理设计的,而启动模板则基于DDD层进行分层。因此,本指南尊重该分层模型,并逐层说明了迁移。

创建解决方案

迁移的第一步是创建一个新的解决方案。我们建议您使用启动模板创建一个全新的项目(请参阅本文档了解ABP商业版)。

创建项目并运行应用程序后,可以将代码从现有解决方案逐步复制到新解决方案中。

关于预构建模块

ABP框架的启动项目使用预先构建的模块(不是全部,而是基本模块)和主题作为NuGet / NPM软件包。因此,您在解决方案中看不到模块/主题的源代码。这样做的好处是,当发布新版本时,您可以轻松地更新这些软件包。但是,您不能轻松地将它们定制为您手中的源。

我们建议继续将这些模块用作软件包引用,这样您就可以轻松获得新特征(请参阅abp update命令)。在这种情况下,您可以使用一些选项来自定义或扩展所用模块的功能。

  • 您可以创建自己的实体,并与使用的模块中的实体共享同一数据库表。例如,启动模板中包含AppUser实体。
  • 您可以使用自己的实现替换领域服务、应用服务、控制器、页面模型或其他类型的服务。我们建议您从现有的实现中继承并覆盖所需的方法。
  • 你可以替换一个使用了虚拟文件系统的您自己的.cshtml视图、页面、视图组件、部分视图…。
  • 您可以使用虚拟文件系统覆盖javascript、css、图像或任何其他类型的静态文件。

届时将开发和记录更多扩展/自定义选项。但是,如果您需要完全更改模块实现,则最好将相关模块的源代码添加到您自己的解决方案中,并删除程序包依赖。

模块和主题的源代码已获得MIT许可,您可以完全拥有和自定义它,而没有任何限制(对于ABP商业,如果您拥有包含源代码的许可类型,您可以下载模块/主题的源代码)。

领域层

您的大多数领域层代码将保持不变,而您需要对域对象执行一些小的更改。

聚合根和实体

ABP框架和ASP.NET Boilerplate都具有IEntityIEntity<T>接口以及EntityEntity<T>基类来定义实体,但是它们之间存在一些差异。

如果您在ASP.NET Boilerplate应用程序中具有这样的实体:

public class Person : Entity //Default PK is int for the ASP.NET Boilerplate
{
    ...
}

然后,您的主键(基类中的Id属性)就是int,其是 ASP.NET Boilerplate默认主键PK)类型。如果要设置另一种类型的PK,则需要显式声明它:

public class Person : Entity<Guid> //Set explicit PK in the ASP.NET Boilerplate
{
    ...
}

ABP框架的行为有所不同,并且希望始终明确设置 PK 类型:

public class Person : Entity<Guid> //Set explicit PK in the ASP.NET Boilerplate
{
    ...
}

在这个例子中,Id 属性(以及数据库中的相应PK)将是 Guid

复合主键

ABP框架也有一个非通用Entity基类,但是这次它没有Id属性。其目的是允许您创建具有复合PK的实体。请参阅文档以了解有关复合PK的更多信息。

聚合根

现在,最佳做法是使用AggregateRoot基类,而不是Entity用于聚合根实体。请参阅文档以了解有关聚合根的更多信息。

ASP.NET Boilerplate相反,ABP框架仅为聚合根创建默认存储库(IRepository<T>)。它不会为继承自Entity的其他类型创建。

如果仍然要为所有实体类型创建默认存储库,请在解决方案中找到YourProjectName EntityFrameworkCoreModule类,然后更改options.AddDefaultRepositories()options.AddDefaultRepositories(includeAllEntities: true)(可能已经类似于应用程序启动模板的类)。

迁移现有实体

对于所有ABP框架模块,我们建议并使用GUID作为PK类型。但是,您可以继续使用现有的PK类型来更轻松地迁移数据库表。

具有挑战性的部分将是与ASP.NET Boilerplate相关的实体的主键,例如用户、角色、租户、设置等。我们的建议是使用工具或手动方式(注意外键值)在数据库中将数据从现有数据库复制到新数据库表中。

文献资料

请参阅文档以获取有关实体的详细信息:

ASP.NET样板-实体文档

ABP框架-实体文档

储存库

ABP框架仅为聚合根创建默认存储库(IRepository<T>)。它不会为继承自Entity的其他类型创建。有关更多信息,请参见上面的“聚合根”部分。

ABP框架和ASP.NET Boilerplate都具有默认的通用存储库系统,但是有一些区别。

注入存储库

ASP.NET Boilerplate中,您可以直接注入和使用两个默认的存储库接口:

  • IRepository<TEntity>(例如IRepository<Person>)用于具有int主键(PK)(默认PK类型)的实体。
  • IRepository<TEntity, TKey>(例如IRepository<Person, Guid>)用于具有其他类型PK的实体。

ABP框架没有默认的PK类型,因此您需要显式声明实体的PK类型,例如IRepository<Person, int>IRepository<Person, Guid>

ABP框架也具有IRepository<TEntity>(没有PK),但通常在您的实体具有复合PK时使用(因为此存储库没有适用于Id属性的方法)。请参阅文档以了解有关复合PK的更多信息。

受限存储库

ABP框架还提供了一些存储库接口:

  • IBasicRepository<TEntity, TKey>IRepository具有相同的方法,除了后者没有IQueryable支持。如果您不想向应用程序层公开复杂的查询代码,则该功能很有用。在这种情况下,您通常希望创建自定义存储库以封装查询逻辑。对于不支持IQueryable的数据库提供程序也很有用。
  • IReadOnlyRepository<TEntity,TKey> 具有从数据库获取数据的方法,但不包含任何更改数据库的方法。
  • IReadOnlyBasicRepository<TEntity, TKey>与只读存储库相似,但不支持IQueryable

所有接口都有不带TKey的版本(例如IReadOnlyRepository),它们可以像上面解释的那样用于复合PK

GetAll()与IQueryable

ASP.NET Boilerplate的存储库具有一种GetAll()方法,该方法用于获取IQueryable在其上执行LINQ 的对象。一个示例应用程序服务调用该GetAll()方法:

public class PersonAppService : ApplicationService, IPersonAppService
{
    private readonly IRepository<Person, Guid> _personRepository;

    public PersonAppService(IRepository<Person, Guid> personRepository)
    {
        _personRepository = personRepository;
    }

    public async Task DoIt()
    {
        var people = await _personRepository
            .GetAll() //GetAll() returns IQueryable
            .Where(p => p.BirthYear > 2000) //Use LINQ extension methods
            .ToListAsync();
    }
}

ABP框架的存储库没有此方法。相反,它自己实现了IQueryable。因此,您可以在存储库上直接使用LINQ

public class PersonAppService : ApplicationService, IPersonAppService
{
    private readonly IRepository<Person, Guid> _personRepository;

    public PersonAppService(IRepository<Person, Guid> personRepository)
    {
        _personRepository = personRepository;
    }

    public async Task DoIt()
    {
        var people = await _personRepository
            .Where(p => p.BirthYear > 2000) //Use LINQ extension methods
            .ToListAsync();
    }
}

请注意,为了使用异步LINQ扩展方法(如此处的ToListAsync),您可能需要依赖数据库提供程序(如EF Core),因为这些方法是在数据库提供程序包中定义的,它们不是标准的LINQ方法。

FirstOrDefault(predicate), Single()…方法

ABP框架存储库没有这样的方法得到谓词(表达式),因为存储库本身是IQueryable,并且所有这些方法已经是可以直接使用的标准LINQ扩展方法。

但是,它提供了以下方法,可用于通过其Id查询单个实体:

  • FindAsync(id) 返回实体;如果找不到,则返回null。
  • GetAsync(id)方法返回该实体,或者如果找不到则抛出EntityNotFoundException(导致HTTP 404状态代码)。

同步与异步

ABP框架存储库没有同步方法(例如Insert)。所有方法都是异步的(如InsertAsync)。因此,如果您的应用程序具有同步存储库方法用法,请将其转换为异步版本。

通常,ABP框架会强制您在所有地方完全使用异步,因为不建议将异步与同步方法混合使用。

文献资料

请参阅文档以获取有关存储库的详细信息:

领域服务

您的域服务逻辑在迁移过程中基本保持不变。ABP框架还定义了基础的DomainService类,并且IDomainService接口就像ASP.NET Boilerplate一样工作。

应用层

您的应用程序服务逻辑在迁移时仍然相似。ABP框架还定义了基础的ApplicationService类,并且IApplicationService接口就像ASP.NET Boilerplate一样工作,但是在细节方面存在一些差异。

声明式授权

ASP.NET Boilerplate具有用于声明性授权的AbpAuthorizeAbpMvcAuthorize属性。用法示例:

[AbpAuthorize("MyUserDeletionPermissionName")]
public async Task DeleteUserAsync(...)
{
    ...
}

ABP框架没有这样的自定义属性。它在所有层中使用标准的Authorize属性。

[Authorize("MyUserDeletionPermissionName")]
public async Task DeleteUserAsync(...)
{
    ...
}

Microsoft授权扩展库的更好集成可以做到这一点。有关授权系统的更多信息,请参见下面的“授权”部分。

CrudAppService和AsyncCrudAppService类

ASP.NET Boilerplate具有CrudAppService(带有同步服务方法)和AsyncCrudAppService(带有异步服务方法)类。

ABP框架仅只有CrudAppService,而其实际上仅具有异步方法(而不是同步方法)。

ABP框架的CrudAppService方法签名与旧的签名略有不同。例如,旧的更新方法签名为Task<TEntityDto> UpdateAsync(TUpdateInput input),而新的签名方法为Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input)。主要区别在于它获取更新实体的Id作为单独的参数,而不是包含在输入DTO中。

数据传输对象(DTO)

ABP框架中也有类似的基本DTO类(例如EntityDto)。因此,您可以根据需要找到相应的DTO基类。

验证方式

您可以像使用ASP.NET Boilerplate一样继续使用数据注释属性来验证DTO

ABP框架不包括ASP.NET Boilerplate中存在的ICustomValidate。相反,您应为自定义验证逻辑实现标准IValidatableObject接口。

基础设施层

命名空间

ASP.NET Boilerplate使用Abp.*命名空间,而ABP框架使用框架和预构建基本模块的Volo.Abp.*命名空间。

此外,还有一些预构建的应用程序模块(例如docsblog模块)正在使用Volo.*命名空间(例如Volo.Blogging.*Volo.Docs.*)。我们将这些模块视为由Volosoft开发的独立开源产品,而不是用于完成ABP框架并在应用程序中使用的附加组件或通用模块。我们已经将它们开发为模块,以使其可重复使用,作为更大解决方案的一部分。

模块系统

ASP.NET Boilerplate和ABP框架两者都有AbpModule,尽管他们有所不同。

ASP.NET BoilerplateAbpModule类有你可以覆盖和配置框架和依赖模块的PreInitializeInitializePostInitialize方法。您也可以在这些方法中注册并解析依赖关系。

ABP框架的AbpModule类具有ConfigureServicesOnApplicationInitialization方法(及其PrePost的版本)。它类似于ASP.NET CoreStartup类。您配置其他服务并在ConfigureServices中注册依赖关系。但是,您现在可以在这一点上解决依赖关系。您可以在OnApplicationInitialization方法中解析依赖关系并配置ASP.NET Core管道,而您不能在此处注册依赖关系。因此,新的模块类将依赖关系注册阶段与依赖关系解析阶段分开,因为它遵循ASP.NET Core的方法。

依赖注入

DI框架

ASP.NET Boilerplate使用Castle Windsor作为依赖关系注入框架。这是ASP.NET Boilerplate框架的基本依赖性。我们有很多反馈可以使ASP.NET Boilerplate DI框架不可知,但是由于设计的原因,这并不容易。

ABP框架是独立于依赖注入框架的,因为它使用了Microsoft依赖注入扩展库作为抽象。ABP框架或模块包都不依赖于任何特定的库。

但是,ABP框架不使用Microsoft的基本DI库,因为它缺少ABP框架需要具备的某些特征:属性注入和拦截。所有启动模板和示例都使用Autofac作为DI库,它是ABP框架的唯一官方集成库。如果您没有充分的理由,建议您将Autofac与ABP框架一起使用。如果您有充分的理由,请在GitHub上创建一个issue以请求它,或者只是实现它并发送拉取请求:)

注册依赖关系

注册依赖关系是类似的,并且通常由框架按常规方式处理(如存储库、应用程序服务、控制器等)。为未按约定注册的服务实现相同的ITransientDependencyISingletonDependencyIScopedDependency接口。

当您需要手动注册依赖项时,请在模块的ConfigureServices方法中使用context.Services。示例:

public class BlogModule : AbpModule
{
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        //Register an instance as singleton
        context.Services.AddSingleton<TaxCalculator>(new TaxCalculator(taxRatio: 0.18));

        //Register a factory method that resolves from IServiceProvider
        context.Services.AddScoped<ITaxCalculator>(
            sp => sp.GetRequiredService<TaxCalculator>()
        );
    }
}

有关详细信息,请参见ABP框架依赖注入文档

配置与选项系统

ASP.NET Boilerplate具有自己的配置系统,用于配置框架和模块。例如,您可以在模块Initialize方法中禁用审核日志记录:

public override void Initialize()
{
    Configuration.Auditing.IsEnabled = false;
}

ABP框架使用选项模式来配置框架和模块。您通常可以在模块ConfigureServices方法中配置选项:

public override void ConfigureServices(ServiceConfigurationContext context)
{
    Configure<AbpAuditingOptions>(options =>
    {
        options.IsEnabled = false;
    });
}

除了相关的配置对象,每个模块都有单独的选项类,并且在相关文档中定义了这些特征。

IAbpSession与ICurrentUser和ICurrentTenant

ASP.NET BoilerplateIAbpSession服务用于获取当前的用户和租户信息,例如UserIdTenantId

ABP框架没有相同的服务。而是使用ICurrentUserICurrentTenant服务。这些服务在某些通用类(如ApplicationServiceAbpController)中被定义为基本属性,因此通常不需要手动注入它们。与IAbpSession相比,它们还具有很多属性。

授权

ABP框架通过将权限添加为自动策略来扩展ASP.NET Core授权,并允许授权系统也可在应用服务中使用。

AbpAutorize与Autorize

使用标准[Autorize][AllowAnonymous]属性,而不要使用ASP.NET Boilerplate的自定义[AbpAutorize][AbpAllowAnonymous]属性。

IPermissionChecker vs IAuthorizationService

使用标准IAuthorizationService检查权限,而不是使用ASP.NET BoilerplateIPermissionChecker服务。虽然IPermissionChecker在ABP框架中也存在,但它用于显式使用权限。使用IAuthorizationService是推荐的方式,因为它包括其他类型策略的检查了。

AuthorizationProvider与PermissionDefinitionProvider

您可以从ASP.NET Boilerplate中的AuthorizationProvider继承来定义您的权限。ABP框架将其替换为PermissionDefinitionProvider基类。因此,通过从PermissionDefinitionProvider类继承来定义权限。

工作单位

工作单元系统已设计为无缝工作。在大多数情况下,您无需更改任何内容。

ABP框架的UnitOfWork属性没有ScopeOptionTransactionScopeOption的类型)属性。相反,使用带有requiresNew = trueIUnitOfWorkManager.Begin()方法在事务作用域中创建一个独立的内部事务。

数据过滤器

ASP.NET Boilerplate将数据过滤系统实现为工作单元的一部分。ABP框架具有单独的IDataFilter服务。

请参阅数据过滤文档以了解如何启用/禁用过滤器。

有关UOW系统的更多信息,请参见UOW文档

多租户

IMustHaveTenant和IMayHaveTenant与IMultiTenant

ASP.NET Boilerplate定义IMustHaveTenantIMayHaveTenant接口以为您的实体实现它们。这样,将根据当前租户自动过滤您的实体。由于设计原因,存在一个问题:如果要创建一个非多租户应用程序,就必须在数据库中创建一个“默认”租户,Id为“1”(这个“默认”租户用作单个租户)。

ABP框架具有一个用于多租户实体的接口:IMultiTenant,其定义了Guid类型的可空TenantId属性。如果您的应用程序不是多租户,则您的实体将具有空的TenantId(而不是默认值)。

在迁移时,您需要更改TenantId字段类型,并将这些接口替换为IMultiTenant

在租户之间切换

在某些情况下,您可能需要切换到代码范围作用域的租户并在此作用域内使用租户的数据。

ASP.NET Boilerplate中,使用以下IUnitOfWorkManager服务完成此操作:

public async Task<List<Product>> GetProducts(int tenantId)
{
    using (_unitOfWorkManager.Current.SetTenantId(tenantId))
    {
        return await _productRepository.GetAllListAsync();
    }
}

在ABP框架中,该ICurrentTenant服务是通过以下方式完成的:

public async Task<List<Product>> GetProducts(Guid tenantId)
{
    using (_currentTenant.Change(tenantId))
    {
        return await _productRepository.GetListAsync();
    }
}

null传递给Change方法以切换到主机端。

缓存

ASP.NET Boilerplate具有自己的分布式缓存抽象,该抽象具有内存和Redis实现。通常,您需要注入ICacheManager服务并使用其GetCache(...)方法来获取缓存,然后在缓存中获取和设置对象。

ABP框架使用并扩展了ASP.NET Core分布式缓存抽象。它定义了IDistributedCache<T>用于注入缓存和获取/设置对象的服务。

日志记录

ASP.NET Boilerplate使用Castle Windsor的日志记录工具作为抽象,并支持多个日志记录提供程序,包括Log4Net(默认选项是在启动项目时使用的)和Serilog。通常,您需要对日志记录进行属性注入:

using Castle.Core.Logging; //1: Import Logging namespace

public class TaskAppService : ITaskAppService
{
    //2: Getting a logger using property injection
    public ILogger Logger { get; set; }

    public TaskAppService()
    {
        //3: Do not write logs if no Logger supplied.
        Logger = NullLogger.Instance;
    }

    public void CreateTask(CreateTaskInput input)
    {
        //4: Write logs
        Logger.Info("Creating a new task with description: " + input.Description);
        //...
    }
}

ABP框架依赖于Microsoft的日志记录扩展库,它也是一个抽象,并且有许多提供程序来实现它。启动模板将Serilog用作预配置的日志记录库,而在项目中很容易更改。使用模式类似:

//1: Import the Logging namespaces
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;

public class TaskAppService : ITaskAppService
{
    //2: Getting a logger using property injection
    public ILogger<TaskAppService> Logger { get; set; }

    public TaskAppService()
    {
        //3: Do not write logs if no Logger supplied.
        Logger = NullLogger<TaskAppService>.Instance;
    }

    public void CreateTask(CreateTaskInput input)
    {
        //4: Write logs
        Logger.Info("Creating a new task with description: " + input.Description);
        //...
    }
}

您注入ILogger<T>而不是ILogger

对象到对象的映射

IObjectMapper服务

ASP.NET Boilerplate定义了一个IObjectMapper服务(请参阅参考资料),并且与AutoMappe库集成在一起。

用法示例:使用给定的CreateUserInput对象创建一个User对象:

public void CreateUser(CreateUserInput input)
{
    var user = ObjectMapper.Map<User>(input);
    ...
}

示例:使用给定的UpdateUserInput对象更新现有User属性:

public async Task UpdateUserAsync(Guid id, UpdateUserInput input)
{
    var user = await _userRepository.GetAsync(id);
    ObjectMapper.Map(input, user);
}

ABP框架具有相同的IObjectMapper服务(请参阅参考资料)和AutoMapper集成,但映射方法略有不同。

用法示例:使用给定的CreateUserInput对象创建一个User对象:

public void CreateUser(CreateUserInput input)
{
    var user = ObjectMapper.Map<CreateUserInput, User>(input);
}

这次您需要显式声明源类型和目标类型(而ASP.NET Boilerplate仅需要目标类型)。

示例:使用给定的UpdateUserInput对象更新现有User属性:

public async Task UpdateUserAsync(Guid id, UpdateUserInput input)
{
    var user = await _userRepository.GetAsync(id);
    ObjectMapper.Map<UpdateUserInput, User>(input, user);
}

同样,ABP框架希望明确设置源和目标类型。

AutoMapper集成

自动映射属性

ASP.NET BoilerplateAutoMapToAutoMapFromAutoMap属性,可以自动为声明的类型创建映射。例:

[AutoMapTo(typeof(User))]
public class CreateUserInput
{
    public string Name { get; set; }
    public string Surname { get; set; }
    ...
}

ABP框架没有此类属性,因为AutoMapper现在也是一个类似的属性。您需要切换到AutoMapper的属性。

映射定义

ABP框架严格遵循AutoMapper原理。您可以定义派生自Profile类的类以定义映射。

配置验证

配置验证是AutoMapper以安全方式维护映射配置的最佳实践。

请参阅文档以获取与对象映射有关的更多信息。

设置管理

定义设置

在基于ASP.NET Boilerplate的应用程序中,您将创建一个派生自SettingProvider类的类,实现GetSettingDefinitions方法并将您的类添加到Configuration.Settings.Providers列表中。

在ABP框架中,您需要从SettingDefinitionProvider派生您的类并实现Define方法。您不需要注册您的类,因为ABP框架会自动发现它。

获取设置值

ASP.NET Boilerplate提供了ISettingManager在服务器端读取设置值和在JavaScript端读取设置值abp.setting.get(...)方法。

ABP框架在服务器端有读取设置值的ISettingProvider服务,在JavaScript端有abp.setting.get(...)方法。

设置设定值

对于ASP.NET Boilerplate,您使用相同的ISettingManager服务来更改设置值。

ABP框架将其分开,并提供设置管理模块(已预添加到启动项目中),该模块具有ISettingManager用于更改设置值。引入这种分离是为了支持分层部署方案(在这种情况下ISettingProvider也可以在客户端应用程序中 工作,同时ISettingManager也可以在服务器(API)端工作)。

时钟

ASP.NET Boilerplate有一个静态Clock服务(请参阅参考资料),用于抽象DateTime类型,因此您可以轻松地在本地时间和UTC时间之间进行切换。您无需注入它,而只需使用Clock.Now静态方法即可获取当前时间。

ABP框架提供了具有类似目标的IClock服务(请参阅参考资料),但是现在您需要在需要时注入它。

事件总线

ASP.NET Boilerplate具有一个进程内事件总线系统。通常,您可以注入IEventBus(或使用静态实例EventBus.Default)触发事件。它会自动触发实体更改事件(例如EntityCreatingEventDataEntityUpdatedEventData)。您可以通过实现IEventHandler<T>接口来创建一个类。

ABP框架将事件总线分为两个服务:ILocalEventBusIDistributedEventBus

本地事件总线类似于ASP.NET Boilerplate的事件总线,而分布式事件总线是ABP框架中引入的新功能。

因此,要迁移代码;

  • 使用ILocalEventBus代替IEventBus
  • 实现ILocalEventHandler而不是IEventHandler

注意,ABP框架也有一个IEventBus接口,但是它确实是本地和分布式事件总线的通用接口。它不被注入并且被直接使用。

特征管理

在多租户应用程序中使用特征系统来定义应用程序的特征,检查给定的特征是否对当前租户可用。

定义特征

ASP.NET Boilerplate请参阅参考资料)中,创建一个从FeatureProvider继承的类,重写SetFeatures方法,然后将您的类添加到Configuration.Features.Providers列表中。

在ABP框架(请参阅参考资料)中,您从FeatureDefinitionProvider派生了您的类并重写了Define方法。无需将类添加到配置中,框架会自动发现它。

检查特征

您可以继续使用RequiresFeature属性和IFeatureChecker服务来检查是否为当前租户启用了特征。

更改特征值

在ABP框架中,您可以使用IFeatureManager更改租户的特征值。

审计日志

ASP.NET Boilerplate请参阅参考资料)和ABP框架(请参阅参考资料)具有类似的审核日志系统。ABP框架要求将UseAuditing()中间件添加到ASP.NET Core管道中,该管道已添加到启动模板中。因此,大多数情况下,它都是开箱即用的。

本地化

ASP.NET Boilerplate支持XML和JSON文件来定义UI的本地化键值对(请参阅参考资料)。ABP框架仅支持JSON格式化程序本地化文件(请参阅参考资料)。因此,您需要将XML文件转换为JSON。

ASP.NET Boilerplate拥有自己的ILocalizationManager服务,可将其注入并用于服务器端的本地化。

ABP框架使用Microsoft本地化扩展库,因此已完全集成到ASP.NET Core。您使用IStringLocalizer<T>服务来获取本地化的文本。示例:

public class MyService
{
    private readonly IStringLocalizer<TestResource> _localizer;

    public MyService(IStringLocalizer<TestResource> localizer)
    {
        _localizer = localizer;
    }

    public void Foo()
    {
        var str = _localizer["HelloWorld"]; //Get a localized text
    }
}

因此,您需要用IStringLocalizer代替ILocalizationManager

它还提供了客户端使用的API:

var testResource = abp.localization.getResource('Test');
var str = testResource('HelloWorld');

就像在ASP.NET Boilerplate中的abp.localization.localize(...)一样。

导航与菜单

在ASP.NET中,您将创建一个派生自NavigationProvider的类,以定义菜单元素。菜单项具有限制访问菜单元素的requiredPermissionName属性。菜单项是静态的,您的类仅执行一次。

在创建类所需的ABP框架中,实现了IMenuContributor接口。每当需要渲染菜单时,都将执行您的类。因此,您可以有条件地添加菜单项。

例如,这是租户管理模块的菜单贡献者:

public class AbpTenantManagementWebMainMenuContributor : IMenuContributor
{
    public async Task ConfigureMenuAsync(MenuConfigurationContext context)
    {
        //Add items only to the main menu
        if (context.Menu.Name != StandardMenus.Main)
        {
            return;
        }

        //Get the standard administration menu item
        var administrationMenu = context.Menu.GetAdministration();

        //Resolve some needed services from the DI container
        var l = context.GetLocalizer<AbpTenantManagementResource>();

        var tenantManagementMenuItem = new ApplicationMenuItem(
            TenantManagementMenuNames.GroupName,
            l["Menu:TenantManagement"],
            icon: "fa fa-users");

        administrationMenu.AddItem(tenantManagementMenuItem);

        //Conditionally add the "Tenants" menu item based on the permission
        if (await context.IsGrantedAsync(TenantManagementPermissions.Tenants.Default))
        {
            tenantManagementMenuItem.AddItem(
                new ApplicationMenuItem(
                    TenantManagementMenuNames.Tenants,
                    l["Tenants"],
                    url: "/TenantManagement/Tenants"));
        }
    }
}

因此,如果只想在用户具有相关权限时才显示菜单项,则需要使用IAuthorizationService来检查权限。

导航/菜单系统仅适用于ASP.NET Core MVC / Razor Pages应用程序。Angular应用程序在启动模板中实现了不同的系统。

缺少功能

ABP框架不具备以下特征。这里,列出了一些主要的缺失特征(以及在ABP框架GitHub存储库上等待该特征的相关issue):

其中某些特征最终将实现。但是,如果它们对您很重要,则可以自己实现它们。如果需要,您可以为该框架做出贡献,我们感激不尽。

 类似资料: