文章目录
介绍我应该迁移吗?那 Zero呢? MVC 5.x项目迁移进度创建解决方案关于预构建模块领域层聚合根和实体复合主键聚合根迁移现有实体文献资料储存库注入存储库受限存储库GetAll()与IQueryableFirstOrDefault(predicate), Single()...方法同步与异步文献资料领域服务应用层声明式授权CrudAppService和AsyncCrudAppService类数据传输对象(DTO)验证方式基础设施层命名空间模块系统依赖注入DI框架注册依赖关系配置与选项系统IAbpSession与ICurrentUser和ICurrentTenant授权AbpAutorize与AutorizeIPermissionChecker vs IAuthorizationServiceAuthorizationProvider与PermissionDefinitionProvider工作单位数据过滤器多租户IMustHaveTenant和IMayHaveTenant与IMultiTenant在租户之间切换缓存日志记录对象到对象的映射IObjectMapper服务AutoMapper集成自动映射属性映射定义配置验证设置管理定义设置获取设置值设置设定值时钟事件总线特征管理定义特征检查特征更改特征值审计日志本地化导航与菜单缺少功能ABP框架是开源 Boilerplate框架的继承者。本指南旨在帮助您将现有解决方案(使用Boilerplate
框架开发)迁移到ABP框架。
介绍
自以来,Boilerplate
一直在积极开发中。它受到社区的喜爱、使用和贡献。它最初是作为开发人员的附带项目进行的,但现在,除了强大的社区支持之外,它还由Volosoft公司正式维护和改进。
ABP框架的目标与Boilerplate
框架相同:不要重复自己!它提供了基础结构、工具和启动模板,使开发人员在开发企业软件解决方案时更加轻松。
如果您想知道为什么我们需要重新编写Boilerplate
框架,请参阅介绍博客文章。
我应该迁移吗?
不,您不必!
Boilerplate
仍在积极开发和维护中。它还可以在最新的Core
和相关的库和工具上运行。它是最新的。
但是,如果您想利用新的ABP框架特征和新的架构机会(例如对NoSQL
数据库的支持,微服务兼容性,高级模块化),您可以将该文档用作指南。
那 Zero呢?
Zero是由Boilerplate
核心团队在Boilerplate
框架之上开发的商业产品。它提供了预构建的应用程序特征,代码生成工具和外观漂亮的现代UI。它得到了全球数千家公司的信任和使用。
我们创建了ABP商业版,以替代Zero
。与Zero
相比,ABP商业版具有更高的模块化和可升级性。与Zero
相比,它目前具有较少的特征,但是随着时间的推移,差距将逐渐缩小(它还具有Zero
中不存在的某些特征)。
我们认为,在启动新应用程序时,Zero
仍然是一个不错的选择。它是可用于生产的成熟解决方案,可以作为完整的源代码交。它正在积极开发中,我们正在不断添加新特征。
我们建议您不要将基于Zero
的解决方案迁移到ABP商业版,如果:
您的Zero
解决方案已经成熟,并且正在维护中,而不是快速的发展。您没有足够的开发时间来执行迁移。整体解决方案适合您的业务。您已经根据需要自定义了太多现有的Zero
功能。
我们还建议您根据需要比较两种产品的特征。
如果您有一个基于Zero
的解决方案,并且想要迁移到ABP商业版,则本指南也将为您提供帮助。
MVC 5.x项目
ABP框架不支持MVC 5.x
,仅适用于Core
。因此,如果您迁移基于MVC 5.x
的项目,则还将处理.NET Core
迁移。
迁移进度
我们通过获取Boilerplate
框架的最佳组成部分来设计ABP框架,因此,如果您开发了基于Boilerplate
的应用程序,则对您来说会很熟悉。
在Boilerplate
中,我们在UI方面没有做很多工作,但是使用了一些免费的主题(在另一方面,Zero
使用了metronic主题)。在ABP框架中,我们在UI方面做了很多工作(尤其是对于MVC / Razor Pages UI,因为Angular已经拥有了自己的良好模块化系统)。因此,迁移中最具挑战性的部分将是解决方案的用户界面。
ABP框架(和Boilerplate
)是基于领域驱动设计模式和原理设计的,而启动模板则基于DDD层进行分层。因此,本指南尊重该分层模型,并逐层说明了迁移。
创建解决方案
迁移的第一步是创建一个新的解决方案。我们建议您使用启动模板创建一个全新的项目(请参阅本文档了解ABP商业版)。
创建项目并运行应用程序后,可以将代码从现有解决方案逐步复制到新解决方案中。
关于预构建模块
ABP框架的启动项目使用预先构建的模块(不是全部,而是基本模块)和主题作为NuGet / NPM软件包。因此,您在解决方案中看不到模块/主题的源代码。这样做的好处是,当发布新版本时,您可以轻松地更新这些软件包。但是,您不能轻松地将它们定制为您手中的源。
我们建议继续将这些模块用作软件包引用,这样您就可以轻松获得新特征(请参阅abp update命令)。在这种情况下,您可以使用一些选项来自定义或扩展所用模块的功能。
您可以创建自己的实体,并与使用的模块中的实体共享同一数据库表。例如,启动模板中包含AppUser
实体。您可以使用自己的实现替换领域服务、应用服务、控制器、页面模型或其他类型的服务。我们建议您从现有的实现中继承并覆盖所需的方法。你可以替换一个使用了虚拟文件系统的您自己的.cshtml
视图、页面、视图组件、部分视图…。您可以使用虚拟文件系统覆盖javascript、css、图像或任何其他类型的静态文件。
届时将开发和记录更多扩展/自定义选项。但是,如果您需要完全更改模块实现,则最好将相关模块的源代码添加到您自己的解决方案中,并删除程序包依赖。
模块和主题的源代码已获得MIT许可,您可以完全拥有和自定义它,而没有任何限制(对于ABP商业,如果您拥有包含源代码的许可类型,您可以下载模块/主题的源代码)。
领域层
您的大多数领域层代码将保持不变,而您需要对域对象执行一些小的更改。
聚合根和实体
ABP框架和Boilerplate
都具有IEntity
和IEntity<T>
接口以及Entity
和Entity<T>
基类来定义实体,但是它们之间存在一些差异。
如果您在Boilerplate
应用程序中具有这样的实体:
public class Person : Entity //Default PK is int for the Boilerplate{...}
然后,您的主键(基类中的Id
属性)就是int
,其是Boilerplate
的默认主键(PK
)类型。如果要设置另一种类型的PK
,则需要显式声明它:
public class Person : Entity<Guid> //Set explicit PK in the Boilerplate{...}
ABP框架的行为有所不同,并且希望始终明确设置PK
类型:
public class Person : Entity<Guid> //Set explicit PK in the Boilerplate{...}
在这个例子中,Id
属性(以及数据库中的相应PK)将是Guid
。
复合主键
ABP框架也有一个非通用Entity
基类,但是这次它没有Id
属性。其目的是允许您创建具有复合PK
的实体。请参阅文档以了解有关复合PK的更多信息。
聚合根
现在,最佳做法是使用AggregateRoot
基类,而不是Entity
用于聚合根实体。请参阅文档以了解有关聚合根的更多信息。
与Boilerplate
相反,ABP框架仅为聚合根创建默认存储库(IRepository<T>
)。它不会为继承自Entity
的其他类型创建。
如果仍然要为所有实体类型创建默认存储库,请在解决方案中找到YourProjectName EntityFrameworkCoreModule
类,然后更改options.AddDefaultRepositories()
为options.AddDefaultRepositories(includeAllEntities: true)
(可能已经类似于应用程序启动模板的类)。
迁移现有实体
对于所有ABP框架模块,我们建议并使用GUID
作为PK
类型。但是,您可以继续使用现有的PK
类型来更轻松地迁移数据库表。
具有挑战性的部分将是与 Boilerplate相关的实体的主键,例如用户、角色、租户、设置等。我们的建议是使用工具或手动方式(注意外键值)在数据库中将数据从现有数据库复制到新数据库表中。
文献资料
请参阅文档以获取有关实体的详细信息:
样板-实体文档
ABP框架-实体文档
储存库
ABP框架仅为聚合根创建默认存储库(
IRepository<T>
)。它不会为继承自Entity
的其他类型创建。有关更多信息,请参见上面的“聚合根”部分。
ABP框架和Boilerplate
都具有默认的通用存储库系统,但是有一些区别。
注入存储库
在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
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框架会强制您在所有地方完全使用异步,因为不建议将异步与同步方法混合使用。
文献资料
请参阅文档以获取有关存储库的详细信息:
Boilerplate-存储库文档
ABP框架-存储库文档
领域服务
您的域服务逻辑在迁移过程中基本保持不变。ABP框架还定义了基础的DomainService
类,并且IDomainService
接口就像Boilerplate
一样工作。
应用层
您的应用程序服务逻辑在迁移时仍然相似。ABP框架还定义了基础的ApplicationService
类,并且IApplicationService
接口就像Boilerplate
一样工作,但是在细节方面存在一些差异。
声明式授权
Boilerplate
具有用于声明性授权的AbpAuthorize
和AbpMvcAuthorize
属性。用法示例:
[AbpAuthorize("MyUserDeletionPermissionName")]public async Task DeleteUserAsync(...){...}
ABP框架没有这样的自定义属性。它在所有层中使用标准的Authorize
属性。
[Authorize("MyUserDeletionPermissionName")]public async Task DeleteUserAsync(...){...}
与Microsoft
授权扩展库的更好集成可以做到这一点。有关授权系统的更多信息,请参见下面的“授权”部分。
CrudAppService和AsyncCrudAppService类
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
基类。
验证方式
您可以像使用Boilerplate
一样继续使用数据注释属性来验证DTO
。
ABP框架不包括Boilerplate
中存在的ICustomValidate
。相反,您应为自定义验证逻辑实现标准IValidatableObject
接口。
基础设施层
命名空间
Boilerplate
使用Abp.*
命名空间,而ABP框架使用框架和预构建基本模块的Volo.Abp.*
命名空间。
此外,还有一些预构建的应用程序模块(例如docs
和blog
模块)正在使用Volo.*
命名空间(例如Volo.Blogging.*
和Volo.Docs.*
)。我们将这些模块视为由Volosoft
开发的独立开源产品,而不是用于完成ABP框架并在应用程序中使用的附加组件或通用模块。我们已经将它们开发为模块,以使其可重复使用,作为更大解决方案的一部分。
模块系统
Boilerplate
和ABP框架两者都有AbpModule
,尽管他们有所不同。
Boilerplate
的AbpModule
类有你可以覆盖和配置框架和依赖模块的PreInitialize
,Initialize
和PostInitialize
方法。您也可以在这些方法中注册并解析依赖关系。
ABP框架的AbpModule
类具有ConfigureServices
和OnApplicationInitialization
方法(及其Pre
和Post
的版本)。它类似于Core
的Startup
类。您配置其他服务并在ConfigureServices
中注册依赖关系。但是,您现在可以在这一点上解决依赖关系。您可以在OnApplicationInitialization
方法中解析依赖关系并配置Core
管道,而您不能在此处注册依赖关系。因此,新的模块类将依赖关系注册阶段与依赖关系解析阶段分开,因为它遵循Core
的方法。
依赖注入
DI框架
Boilerplate
使用Castle Windsor作为依赖关系注入框架。这是Boilerplate
框架的基本依赖性。我们有很多反馈可以使Boilerplate
DI框架不可知,但是由于设计的原因,这并不容易。
ABP框架是独立于依赖注入框架的,因为它使用了Microsoft
的依赖注入扩展库作为抽象。ABP框架或模块包都不依赖于任何特定的库。
但是,ABP框架不使用Microsoft
的基本DI库,因为它缺少ABP框架需要具备的某些特征:属性注入和拦截。所有启动模板和示例都使用Autofac作为DI库,它是ABP框架的唯一官方集成库。如果您没有充分的理由,建议您将Autofac
与ABP框架一起使用。如果您有充分的理由,请在GitHub
上创建一个issue以请求它,或者只是实现它并发送拉取请求:)
注册依赖关系
注册依赖关系是类似的,并且通常由框架按常规方式处理(如存储库、应用程序服务、控制器等)。为未按约定注册的服务实现相同的ITransientDependency
,ISingletonDependency
和IScopedDependency
接口。
当您需要手动注册依赖项时,请在模块的ConfigureServices
方法中使用context.Services
。示例:
public class BlogModule : AbpModule{public override void ConfigureServices(ServiceConfigurationContext context){//Register an instance as singletoncontext.Services.AddSingleton<TaxCalculator>(new TaxCalculator(taxRatio: 0.18));//Register a factory method that resolves from IServiceProvidercontext.Services.AddScoped<ITaxCalculator>(sp => sp.GetRequiredService<TaxCalculator>());}}
有关详细信息,请参见ABP框架依赖注入文档。
配置与选项系统
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
Boilerplate
的IAbpSession
服务用于获取当前的用户和租户信息,例如UserId
和TenantId
。
ABP框架没有相同的服务。而是使用ICurrentUser
和ICurrentTenant
服务。这些服务在某些通用类(如ApplicationService
和AbpController
)中被定义为基本属性,因此通常不需要手动注入它们。与IAbpSession
相比,它们还具有很多属性。
授权
ABP框架通过将权限添加为自动策略来扩展 Core授权,并允许授权系统也可在应用服务中使用。
AbpAutorize与Autorize
使用标准[Autorize]
和[AllowAnonymous]
属性,而不要使用Boilerplate
的自定义[AbpAutorize]
和[AbpAllowAnonymous]
属性。
IPermissionChecker vs IAuthorizationService
使用标准IAuthorizationService
检查权限,而不是使用Boilerplate
的IPermissionChecker
服务。虽然IPermissionChecker
在ABP框架中也存在,但它用于显式使用权限。使用IAuthorizationService
是推荐的方式,因为它包括其他类型策略的检查了。
AuthorizationProvider与PermissionDefinitionProvider
您可以从 Boilerplate中的AuthorizationProvider
继承来定义您的权限。ABP框架将其替换为PermissionDefinitionProvider
基类。因此,通过从PermissionDefinitionProvider
类继承来定义权限。
工作单位
工作单元系统已设计为无缝工作。在大多数情况下,您无需更改任何内容。
ABP框架的UnitOfWork
属性没有ScopeOption
(TransactionScopeOption
的类型)属性。相反,使用带有requiresNew = true
的IUnitOfWorkManager.Begin()
方法在事务作用域中创建一个独立的内部事务。
数据过滤器
Boilerplate
将数据过滤系统实现为工作单元的一部分。ABP框架具有单独的IDataFilter
服务。
请参阅数据过滤文档以了解如何启用/禁用过滤器。
有关UOW系统的更多信息,请参见UOW文档。
多租户
IMustHaveTenant和IMayHaveTenant与IMultiTenant
Boilerplate
定义IMustHaveTenant
和IMayHaveTenant
接口以为您的实体实现它们。这样,将根据当前租户自动过滤您的实体。由于设计原因,存在一个问题:如果要创建一个非多租户应用程序,就必须在数据库中创建一个“默认”租户,Id为“1”(这个“默认”租户用作单个租户)。
ABP框架具有一个用于多租户实体的接口:IMultiTenant
,其定义了Guid
类型的可空TenantId
属性。如果您的应用程序不是多租户,则您的实体将具有空的TenantId(而不是默认值)。
在迁移时,您需要更改TenantId
字段类型,并将这些接口替换为IMultiTenant
。
在租户之间切换
在某些情况下,您可能需要切换到代码范围作用域的租户并在此作用域内使用租户的数据。
在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
方法以切换到主机端。
缓存
Boilerplate
具有自己的分布式缓存抽象,该抽象具有内存和Redis
实现。通常,您需要注入ICacheManager
服务并使用其GetCache(...)
方法来获取缓存,然后在缓存中获取和设置对象。
ABP框架使用并扩展了Core
的分布式缓存抽象。它定义了IDistributedCache<T>
用于注入缓存和获取/设置对象的服务。
日志记录
Boilerplate
使用Castle Windsor的日志记录工具作为抽象,并支持多个日志记录提供程序,包括Log4Net(默认选项是在启动项目时使用的)和Serilog。通常,您需要对日志记录进行属性注入:
using Castle.Core.Logging; //1: Import Logging namespacepublic class TaskAppService : ITaskAppService{//2: Getting a logger using property injectionpublic 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 logsLogger.Info("Creating a new task with description: " + input.Description);//...}}
ABP框架依赖于Microsoft的日志记录扩展库,它也是一个抽象,并且有许多提供程序来实现它。启动模板将Serilog
用作预配置的日志记录库,而在项目中很容易更改。使用模式类似:
//1: Import the Logging namespacesusing Microsoft.Extensions.Logging;using Microsoft.Extensions.Logging.Abstractions;public class TaskAppService : ITaskAppService{//2: Getting a logger using property injectionpublic 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 logsLogger.Info("Creating a new task with description: " + input.Description);//...}}
您注入ILogger<T>
而不是ILogger
。
对象到对象的映射
IObjectMapper服务
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);}
这次您需要显式声明源类型和目标类型(而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集成
自动映射属性
Boilerplate
有AutoMapTo
,AutoMapFrom
和AutoMap
属性,可以自动为声明的类型创建映射。例:
[AutoMapTo(typeof(User))]public class CreateUserInput{public string Name { get; set; }public string Surname { get; set; }...}
ABP框架没有此类属性,因为AutoMapper现在也是一个类似的属性。您需要切换到AutoMapper的属性。
映射定义
ABP框架严格遵循AutoMapper
原理。您可以定义派生自Profile
类的类以定义映射。
配置验证
配置验证是AutoMapper
以安全方式维护映射配置的最佳实践。
请参阅文档以获取与对象映射有关的更多信息。
设置管理
定义设置
在基于Boilerplate
的应用程序中,您将创建一个派生自SettingProvider
类的类,实现GetSettingDefinitions
方法并将您的类添加到Configuration.Settings.Providers
列表中。
在ABP框架中,您需要从SettingDefinitionProvider
派生您的类并实现Define
方法。您不需要注册您的类,因为ABP框架会自动发现它。
获取设置值
Boilerplate
提供了ISettingManager
在服务器端读取设置值和在JavaScript端读取设置值abp.setting.get(...)
方法。
ABP框架在服务器端有读取设置值的ISettingProvider
服务,在JavaScript端有abp.setting.get(...)
方法。
设置设定值
对于Boilerplate
,您使用相同的ISettingManager
服务来更改设置值。
ABP框架将其分开,并提供设置管理模块(已预添加到启动项目中),该模块具有ISettingManager
用于更改设置值。引入这种分离是为了支持分层部署方案(在这种情况下ISettingProvider
也可以在客户端应用程序中 工作,同时ISettingManager
也可以在服务器(API)端工作)。
时钟
Boilerplate
有一个静态Clock
服务(请参阅参考资料),用于抽象DateTime
类型,因此您可以轻松地在本地时间和UTC
时间之间进行切换。您无需注入它,而只需使用Clock.Now
静态方法即可获取当前时间。
ABP框架提供了具有类似目标的IClock
服务(请参阅参考资料),但是现在您需要在需要时注入它。
事件总线
Boilerplate
具有一个进程内事件总线系统。通常,您可以注入IEventBus
(或使用静态实例EventBus.Default
)触发事件。它会自动触发实体更改事件(例如EntityCreatingEventData
和EntityUpdatedEventData
)。您可以通过实现IEventHandler<T>
接口来创建一个类。
ABP框架将事件总线分为两个服务:ILocalEventBus
和IDistributedEventBus
。
本地事件总线类似于Boilerplate
的事件总线,而分布式事件总线是ABP框架中引入的新功能。
因此,要迁移代码;
使用ILocalEventBus
代替IEventBus
。实现ILocalEventHandler
而不是IEventHandler
。
注意,ABP框架也有一个
IEventBus
接口,但是它确实是本地和分布式事件总线的通用接口。它不被注入并且被直接使用。
特征管理
在多租户应用程序中使用特征系统来定义应用程序的特征,检查给定的特征是否对当前租户可用。
定义特征
在Boilerplate
(请参阅参考资料)中,创建一个从FeatureProvider
继承的类,重写SetFeatures
方法,然后将您的类添加到Configuration.Features.Providers
列表中。
在ABP框架(请参阅参考资料)中,您从FeatureDefinitionProvider
派生了您的类并重写了Define
方法。无需将类添加到配置中,框架会自动发现它。
检查特征
您可以继续使用RequiresFeature
属性和IFeatureChecker
服务来检查是否为当前租户启用了特征。
更改特征值
在ABP框架中,您可以使用IFeatureManager
更改租户的特征值。
审计日志
Boilerplate
(请参阅参考资料)和ABP框架(请参阅参考资料)具有类似的审核日志系统。ABP框架要求将UseAuditing()
中间件添加到Core
管道中,该管道已添加到启动模板中。因此,大多数情况下,它都是开箱即用的。
本地化
Boilerplate
支持XML和JSON文件来定义UI的本地化键值对(请参阅参考资料)。ABP框架仅支持JSON格式化程序本地化文件(请参阅参考资料)。因此,您需要将XML文件转换为JSON。
Boilerplate
拥有自己的ILocalizationManager
服务,可将其注入并用于服务器端的本地化。
ABP框架使用Microsoft本地化扩展库,因此已完全集成到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');
就像在 Boilerplate中的abp.localization.localize(...)
一样。
导航与菜单
在中,您将创建一个派生自NavigationProvider
的类,以定义菜单元素。菜单项具有限制访问菜单元素的requiredPermissionName
属性。菜单项是静态的,您的类仅执行一次。
在创建类所需的ABP框架中,实现了IMenuContributor
接口。每当需要渲染菜单时,都将执行您的类。因此,您可以有条件地添加菜单项。
例如,这是租户管理模块的菜单贡献者:
public class AbpTenantManagementWebMainMenuContributor : IMenuContributor{public async Task ConfigureMenuAsync(MenuConfigurationContext context){//Add items only to the main menuif (context.Menu.Name != StandardMenus.Main){return;}//Get the standard administration menu itemvar administrationMenu = context.Menu.GetAdministration();//Resolve some needed services from the DI containervar 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 permissionif (await context.IsGrantedAsync(TenantManagementPermissions.Tenants.Default)){tenantManagementMenuItem.AddItem(new ApplicationMenuItem(TenantManagementMenuNames.Tenants,l["Tenants"],url: "/TenantManagement/Tenants"));}}}
因此,如果只想在用户具有相关权限时才显示菜单项,则需要使用IAuthorizationService
来检查权限。
导航/菜单系统仅适用于 Core MVC / Razor Pages应用程序。Angular应用程序在启动模板中实现了不同的系统。
缺少功能
ABP框架不具备以下特征。这里,列出了一些主要的缺失特征(以及在ABP框架GitHub存储库上等待该特征的相关issue):
多语言实体(#1754)实时通知系统(#633)NHibernate集成(#339)-我们不打算为此工作,但是欢迎任何社区贡献。
其中某些特征最终将实现。但是,如果它们对您很重要,则可以自己实现它们。如果需要,您可以为该框架做出贡献,我们感激不尽。