编写和使用可扩展模块
智慧物流系统 基于Abp框架编写,同时自身的模块遵循可扩展模块方式编写,以下给出一些扩展使用的建议。
Module Entity 支持扩展
Domain.Shared
Domain.Shared 应该有 ObjectExtending 的扩展方法。来扩展该模块以及该模块的哪些实体具备扩展的能力。
以Rcs项目为例,ObjectExtending目录下有扩展ConfigureRcs的扩展方法。
public static class RcsModuleExtensionConfigurationDictionaryExtensions
{
public static ModuleExtensionConfigurationDictionary ConfigureRcs(
this ModuleExtensionConfigurationDictionary modules,
Action<RcsModuleExtensionConfiguration> configureAction)
{
return modules.ConfigureModule(
RcsModuleExtensionConsts.ModuleName,
configureAction
);
}
}
RcsModuleExtensionConfiguration提供了Rcs模块哪些Entity可以扩展。
public class RcsModuleExtensionConfiguration : ModuleExtensionConfiguration
{
public RcsModuleExtensionConfiguration ConfigureRobot(
Action<EntityExtensionConfiguration> configureAction)
{
return this.ConfigureEntity(
RcsModuleExtensionConsts.EntityNames.Robot,
configureAction
);
}
}
Domain
Domain 项目应该在PostConfigureServices 方法配置 ApplyEntityConfigurationToEntity 方法。
以下以Rcs模块为例,配置了Robot实体有扩展实体的能力。
public class LogiMasterRcsDomainModule : AbpModule
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public override void PostConfigureServices(ServiceConfigurationContext context)
{
OneTimeRunner.Run(() =>
{
//robot
ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToEntity(
RcsModuleExtensionConsts.ModuleName,
RcsModuleExtensionConsts.EntityNames.Robot,
typeof(Robot)
);
}
}
}
Application.Contracts
Application.Contracts 项目中 PostConfigureServices 方法配置 ApplyEntityConfigurationToApi。
以下以Rcs模块为例,配置接口有哪些Dto配置了扩展属性。
public class LogiMasterRcsApplicationContractsModule : AbpModule
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public override void PostConfigureServices(ServiceConfigurationContext context)
{
OneTimeRunner.Run(() =>
{
//robot
ModuleExtensionConfigurationHelper.ApplyEntityConfigurationToApi(
RcsModuleExtensionConsts.ModuleName,
RcsModuleExtensionConsts.EntityNames.Robot,
getApiTypes: new[] { typeof(RobotDto) },
createApiTypes: new[] { typeof(RobotCreateDto) },
updateApiTypes: new[] { typeof(RobotUpdateDto) }
);
}
}
}
Application
Application项目中,MapperProfile中增加MapExtraProperties,使得AutoMapper可以映射配置的扩展属性。
以下以Rcs模块为例,展示了Robot的对象映射时配置扩展属性。
public RcsApplicationAutoMapperProfile()
{
//Robot
CreateMap<RobotUpdateDto, Robot>(MemberList.Source)
.MapExtraProperties();
CreateMap<Robot, RobotDto>()
.ForMember(dest => dest.GroupName, opt => opt.MapFrom(src => src.RobotGroup.Name))
.MapExtraProperties();
}
EntityFrameworkCore
EntityFrameworkCore 项目中 DbContextModelCreatingExtensions 方法中 为每个实体增加 ApplyObjectExtensionMappings 方法,并在最后的增加对Contextext扩展。
builder.Entity<Robot>(b =>
{
//Configure table & schema name
b.ToTable(RcsDbProperties.DbTablePrefix + "Robots", RcsDbProperties.DbSchema);
b.ConfigureByConvention();
//Properties
b.Property(r => r.Ip).IsRequired().HasMaxLength(RobotConsts.MaxIpLength);
b.Property(r => r.Name).IsRequired().HasMaxLength(RobotConsts.MaxLength);
//Relations
b.HasOne(r => r.RobotGroup)
.WithMany(g => g.Robots)
.IsRequired(false)
.HasForeignKey(g => g.RobotGroupId)
.OnDelete(DeleteBehavior.SetNull);
//Indexes
b.HasIndex(q => q.CreationTime);
b.ApplyObjectExtensionMappings();
});
builder.TryConfigureObjectExtensions<RcsDbContext>();
如何使用实体扩展
Domain.Shared
Domain.Shared 项目中 xxxModuleExtensionConfigurator 方法中,对需要扩展模块实体进行扩展。
以下是MiR项目对Rcs项目中的Position实体进行扩展,添加了别名和站点类型id。
public static class MirModuleExtensionConfigurator
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
OneTimeRunner.Run(() =>
{
ConfigureExistingProperties();
ConfigureExtraProperties();
});
}
private static void ConfigureExtraProperties()
{
//扩展Position 字段 Alias
ObjectExtensionManager.Instance.Modules()
.ConfigureRcs(mir =>
{
mir.ConfigurePosition(position =>
{
position.AddOrUpdateProperty<string?>(PositionExtensionsConsts.AliasPropertyName);
position.AddOrUpdateProperty<int>(PositionExtensionsConsts.TypeIdPropertyName);
});
});
}
}
添加实体的属性往 往需要数据验证,Abp也提供了对扩展属性的自定义验证。
//arc path control point extension
ObjectExtensionManager.Instance.Modules()
.ConfigureRcs(rcsModule =>
{
rcsModule.ConfigurePath(path =>
{
path.AddOrUpdateProperty<string>(
PathConsts.ArcControlPointPropertyName,
options =>
{
// 添加自定义验证
options.Validators.Add(context =>
{
var controlPoint = context.Value as string;
if (controlPoint.IsNullOrEmpty())
{
var localizer = context.ServiceProvider.GetRequiredService<IStringLocalizer<RcsResource>>();
context.ValidationErrors.Add(
new ValidationResult(
localizer["Path:ArcControlPointIsRequired"]
)
);
}
});
}
);
});
});
Blazor
在界面如何需要对扩展字段进行验证,需要继承该FluentValueValidator类,并编写该字段的验证逻辑。
@code
{
// The validation rules (overkill, I know, but very fluent):
FluentValueValidator<string> ccValidator = new FluentValueValidator<string?>(x => x
.NotEmpty()
.Length(5, 100));
}
<MudTextField T="string?"
@bind-Value:get="_updatePositionDto.GetProperty<string?>(PositionExtensionsConsts.AliasPropertyName)"
@bind-Value:set="(value) =>
{
_updatePositionDto.SetProperty(PositionExtensionsConsts.AliasPropertyName,value);
AliasChanged();
}"
Label="@L["Position:Alias"]"
Validation="ccValidator.Validation" />
Domain
如果多处需要使用设置,获取扩展字段, 可以在Domain中设置扩展方法。
以下以MiR项目为例,展示了Position实体的扩展方法。
public static class PositionExtensions
{
public static int GetTypeId(this Position position)
{
return position.GetProperty<int>(PositionExtensionsConsts.TypeIdPropertyName);
}
public static void SetTypeId(this Position position, int typeId)
{
position.SetProperty(PositionExtensionsConsts.TypeIdPropertyName, typeId);
}
}
EntityFrameworkCore
如果需要单独数据库字段。 需要在EntityFrameworkCore 项目中, xxxEfCoreEntityExtensionMappings 方法中配置属性信息,并数据迁移。
以下以MiR模块对Rcs模块Position实体进行扩展,将Alias字段作为独立表字段。
public static class MirEfCoreEntityExtensionMappings
{
private static readonly OneTimeRunner OneTimeRunner = new OneTimeRunner();
public static void Configure()
{
OneTimeRunner.Run(() =>
{
ObjectExtensionManager.Instance
.MapEfCoreProperty<Position, string?>(
Rcs.Positions.PositionExtensionsConsts.AliasPropertyName,
(entityBuilder, propertyBuilder) =>
{
propertyBuilder.HasMaxLength(128);
}
);
});
}
}
最终项目应用数据迁移时,还需要增加EfCoreEntityExtension的配置。
/* This class is needed for EF Core console commands
* (like Add-Migration and Update-Database commands) */
public class InnoLightDbContextFactory : IDesignTimeDbContextFactory<InnoLightDbContext>
{
public InnoLightDbContext CreateDbContext(string[] args)
{
InnoLightEfCoreEntityExtensionMappings.Configure();
MirEfCoreEntityExtensionMappings.Configure();
RcsEfCoreEntityExtensionMappings.Configure();
var configuration = BuildConfiguration();
var builder = new DbContextOptionsBuilder<InnoLightDbContext>()
.UseMySql(configuration.GetConnectionString("Default"), MySqlServerVersion.LatestSupportedServerVersion);
return new InnoLightDbContext(builder.Options);
}
//...
}