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 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商业版,则本指南也将为您提供帮助。
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
视图、页面、视图组件、部分视图…。届时将开发和记录更多扩展/自定义选项。但是,如果您需要完全更改模块实现,则最好将相关模块的源代码添加到您自己的解决方案中,并删除程序包依赖。
模块和主题的源代码已获得MIT许可,您可以完全拥有和自定义它,而没有任何限制(对于ABP商业,如果您拥有包含源代码的许可类型,您可以下载模块/主题的源代码)。
您的大多数领域层代码将保持不变,而您需要对域对象执行一些小的更改。
ABP框架和ASP.NET Boilerplate
都具有IEntity
和IEntity<T>
接口以及Entity
和Entity<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相关的实体的主键,例如用户、角色、租户、设置等。我们的建议是使用工具或手动方式(注意外键值)在数据库中将数据从现有数据库复制到新数据库表中。
请参阅文档以获取有关实体的详细信息:
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
。
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
方法。
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
具有用于声明性授权的AbpAuthorize
和AbpMvcAuthorize
属性。用法示例:
[AbpAuthorize("MyUserDeletionPermissionName")]
public async Task DeleteUserAsync(...)
{
...
}
ABP框架没有这样的自定义属性。它在所有层中使用标准的Authorize
属性。
[Authorize("MyUserDeletionPermissionName")]
public async Task DeleteUserAsync(...)
{
...
}
与Microsoft
授权扩展库的更好集成可以做到这一点。有关授权系统的更多信息,请参见下面的“授权”部分。
ASP.NET Boilerplate
具有CrudAppService
(带有同步服务方法)和AsyncCrudAppService
(带有异步服务方法)类。
ABP框架仅只有CrudAppService
,而其实际上仅具有异步方法(而不是同步方法)。
ABP框架的CrudAppService
方法签名与旧的签名略有不同。例如,旧的更新方法签名为Task<TEntityDto> UpdateAsync(TUpdateInput input)
,而新的签名方法为Task<TGetOutputDto> UpdateAsync(TKey id, TUpdateInput input)
。主要区别在于它获取更新实体的Id
作为单独的参数,而不是包含在输入DTO
中。
ABP框架中也有类似的基本DTO
类(例如EntityDto
)。因此,您可以根据需要找到相应的DTO
基类。
您可以像使用ASP.NET Boilerplate
一样继续使用数据注释属性来验证DTO
。
ABP框架不包括ASP.NET Boilerplate
中存在的ICustomValidate
。相反,您应为自定义验证逻辑实现标准IValidatableObject
接口。
ASP.NET Boilerplate
使用Abp.*
命名空间,而ABP框架使用框架和预构建基本模块的Volo.Abp.*
命名空间。
此外,还有一些预构建的应用程序模块(例如docs
和blog
模块)正在使用Volo.*
命名空间(例如Volo.Blogging.*
和Volo.Docs.*
)。我们将这些模块视为由Volosoft
开发的独立开源产品,而不是用于完成ABP框架并在应用程序中使用的附加组件或通用模块。我们已经将它们开发为模块,以使其可重复使用,作为更大解决方案的一部分。
ASP.NET Boilerplate
和ABP框架两者都有AbpModule
,尽管他们有所不同。
ASP.NET Boilerplate
的AbpModule
类有你可以覆盖和配置框架和依赖模块的PreInitialize
,Initialize
和PostInitialize
方法。您也可以在这些方法中注册并解析依赖关系。
ABP框架的AbpModule
类具有ConfigureServices
和OnApplicationInitialization
方法(及其Pre
和Post
的版本)。它类似于ASP.NET Core
的Startup
类。您配置其他服务并在ConfigureServices
中注册依赖关系。但是,您现在可以在这一点上解决依赖关系。您可以在OnApplicationInitialization
方法中解析依赖关系并配置ASP.NET Core
管道,而您不能在此处注册依赖关系。因此,新的模块类将依赖关系注册阶段与依赖关系解析阶段分开,因为它遵循ASP.NET Core
的方法。
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以请求它,或者只是实现它并发送拉取请求:)
注册依赖关系是类似的,并且通常由框架按常规方式处理(如存储库、应用程序服务、控制器等)。为未按约定注册的服务实现相同的ITransientDependency
,ISingletonDependency
和IScopedDependency
接口。
当您需要手动注册依赖项时,请在模块的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;
});
}
除了相关的配置对象,每个模块都有单独的选项类,并且在相关文档中定义了这些特征。
ASP.NET Boilerplate
的IAbpSession
服务用于获取当前的用户和租户信息,例如UserId
和TenantId
。
ABP框架没有相同的服务。而是使用ICurrentUser
和ICurrentTenant
服务。这些服务在某些通用类(如ApplicationService
和AbpController
)中被定义为基本属性,因此通常不需要手动注入它们。与IAbpSession
相比,它们还具有很多属性。
ABP框架通过将权限添加为自动策略来扩展ASP.NET Core授权,并允许授权系统也可在应用服务中使用。
使用标准[Autorize]
和[AllowAnonymous]
属性,而不要使用ASP.NET Boilerplate
的自定义[AbpAutorize]
和[AbpAllowAnonymous]
属性。
使用标准IAuthorizationService
检查权限,而不是使用ASP.NET Boilerplate
的IPermissionChecker
服务。虽然IPermissionChecker
在ABP框架中也存在,但它用于显式使用权限。使用IAuthorizationService
是推荐的方式,因为它包括其他类型策略的检查了。
您可以从ASP.NET Boilerplate中的AuthorizationProvider
继承来定义您的权限。ABP框架将其替换为PermissionDefinitionProvider
基类。因此,通过从PermissionDefinitionProvider
类继承来定义权限。
工作单元系统已设计为无缝工作。在大多数情况下,您无需更改任何内容。
ABP框架的UnitOfWork
属性没有ScopeOption
(TransactionScopeOption
的类型)属性。相反,使用带有requiresNew = true
的IUnitOfWorkManager.Begin()
方法在事务作用域中创建一个独立的内部事务。
ASP.NET Boilerplate
将数据过滤系统实现为工作单元的一部分。ABP框架具有单独的IDataFilter
服务。
请参阅数据过滤文档以了解如何启用/禁用过滤器。
有关UOW系统的更多信息,请参见UOW文档。
ASP.NET Boilerplate
定义IMustHaveTenant
和IMayHaveTenant
接口以为您的实体实现它们。这样,将根据当前租户自动过滤您的实体。由于设计原因,存在一个问题:如果要创建一个非多租户应用程序,就必须在数据库中创建一个“默认”租户,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
。
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框架希望明确设置源和目标类型。
ASP.NET 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
以安全方式维护映射配置的最佳实践。
请参阅文档以获取与对象映射有关的更多信息。
在基于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
)触发事件。它会自动触发实体更改事件(例如EntityCreatingEventData
和EntityUpdatedEventData
)。您可以通过实现IEventHandler<T>
接口来创建一个类。
ABP框架将事件总线分为两个服务:ILocalEventBus
和IDistributedEventBus
。
本地事件总线类似于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):
其中某些特征最终将实现。但是,如果它们对您很重要,则可以自己实现它们。如果需要,您可以为该框架做出贡献,我们感激不尽。