10 changed files with 477 additions and 466 deletions
@ -1,16 +1,24 @@ |
|||||
using Volo.Abp.Collections; |
using RulesEngine.Models; |
||||
|
|
||||
namespace LINGYUN.Abp.Rules.RulesEngine |
namespace LINGYUN.Abp.Rules.RulesEngine |
||||
{ |
{ |
||||
public class AbpRulesEngineOptions |
public class AbpRulesEngineOptions |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// 是否忽略租户
|
/// 是否忽略租户
|
||||
/// </summary>
|
/// </summary>
|
||||
public bool IgnoreMultiTenancy { get; set; } |
public bool IgnoreMultiTenancy { get; set; } |
||||
|
/// <summary>
|
||||
public AbpRulesEngineOptions() |
/// 规则引擎可配置
|
||||
{ |
/// </summary>
|
||||
} |
public ReSettings Settings { get; set; } |
||||
} |
|
||||
} |
public AbpRulesEngineOptions() |
||||
|
{ |
||||
|
Settings = new ReSettings |
||||
|
{ |
||||
|
NestedRuleExecutionMode = NestedRuleExecutionMode.Performance |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|||||
@ -1,105 +1,104 @@ |
|||||
using Microsoft.Extensions.Caching.Memory; |
using Microsoft.Extensions.Caching.Memory; |
||||
using Microsoft.Extensions.DependencyInjection; |
using Microsoft.Extensions.DependencyInjection; |
||||
using Microsoft.Extensions.FileProviders; |
using Microsoft.Extensions.FileProviders; |
||||
using Microsoft.Extensions.Primitives; |
using Microsoft.Extensions.Primitives; |
||||
using RulesEngine.Models; |
using RulesEngine.Models; |
||||
using System; |
using System; |
||||
using System.Text; |
using System.Text; |
||||
using System.Threading; |
using System.Threading; |
||||
using System.Threading.Tasks; |
using System.Threading.Tasks; |
||||
using Volo.Abp.Json; |
using Volo.Abp.Json; |
||||
|
|
||||
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders |
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders |
||||
{ |
{ |
||||
public abstract class FileProviderWorkflowRulesResolveContributor : WorkflowRulesResolveContributorBase |
public abstract class FileProviderWorkflowRulesResolveContributor : WorkflowRulesResolveContributorBase |
||||
{ |
{ |
||||
protected IMemoryCache RulesCache { get; private set; } |
protected IMemoryCache RulesCache { get; private set; } |
||||
protected IJsonSerializer JsonSerializer { get; private set; } |
protected IJsonSerializer JsonSerializer { get; private set; } |
||||
|
|
||||
protected IFileProvider FileProvider { get; private set; } |
protected IFileProvider FileProvider { get; private set; } |
||||
|
protected FileProviderWorkflowRulesResolveContributor() |
||||
protected FileProviderWorkflowRulesResolveContributor() |
{ |
||||
{ |
} |
||||
} |
|
||||
|
public override void Initialize(RulesInitializationContext context) |
||||
public override void Initialize(RulesInitializationContext context) |
{ |
||||
{ |
Initialize(context.ServiceProvider); |
||||
Initialize(context.ServiceProvider); |
|
||||
|
RulesCache = context.GetRequiredService<IMemoryCache>(); |
||||
RulesCache = context.ServiceProvider.GetRequiredService<IMemoryCache>(); |
JsonSerializer = context.GetRequiredService<IJsonSerializer>(); |
||||
JsonSerializer = context.ServiceProvider.GetRequiredService<IJsonSerializer>(); |
|
||||
|
FileProvider = BuildFileProvider(context); |
||||
FileProvider = BuildFileProvider(context); |
} |
||||
} |
|
||||
|
protected virtual void Initialize(IServiceProvider serviceProvider) |
||||
protected virtual void Initialize(IServiceProvider serviceProvider) |
{ |
||||
{ |
} |
||||
} |
|
||||
|
protected abstract IFileProvider BuildFileProvider(RulesInitializationContext context); |
||||
protected abstract IFileProvider BuildFileProvider(RulesInitializationContext context); |
|
||||
|
public override async Task ResolveAsync(IWorkflowRulesResolveContext context) |
||||
public override async Task ResolveAsync(IWorkflowRulesResolveContext context) |
{ |
||||
{ |
if (FileProvider != null) |
||||
if (FileProvider != null) |
{ |
||||
{ |
context.WorkflowRules = await GetCachedRulesAsync(context.Type); |
||||
context.WorkflowRules = await GetCachedRulesAsync(context.Type); |
} |
||||
} |
context.Handled = true; |
||||
context.Handled = true; |
} |
||||
} |
|
||||
|
public override void Shutdown() |
||||
public override void Shutdown() |
{ |
||||
{ |
if (FileProvider != null && FileProvider is IDisposable resource) |
||||
if (FileProvider != null && FileProvider is IDisposable resource) |
{ |
||||
{ |
resource.Dispose(); |
||||
resource.Dispose(); |
} |
||||
} |
} |
||||
} |
|
||||
|
private async Task<WorkflowRules[]> GetCachedRulesAsync(Type type, CancellationToken cancellationToken = default) |
||||
private async Task<WorkflowRules[]> GetCachedRulesAsync(Type type, CancellationToken cancellationToken = default) |
{ |
||||
{ |
cancellationToken.ThrowIfCancellationRequested(); |
||||
cancellationToken.ThrowIfCancellationRequested(); |
|
||||
|
var ruleId = GetRuleId(type); |
||||
var ruleId = GetRuleId(type); |
|
||||
|
return await RulesCache.GetOrCreateAsync(ruleId, |
||||
return await RulesCache.GetOrCreateAsync(ruleId, |
async (entry) => |
||||
async (entry) => |
{ |
||||
{ |
entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(30)); |
||||
entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(30)); |
|
||||
|
return await GetFileSystemRulesAsync(type, cancellationToken); |
||||
return await GetFileSystemRulesAsync(type, cancellationToken); |
}); |
||||
}); |
} |
||||
} |
protected abstract int GetRuleId(Type type); |
||||
protected abstract int GetRuleId(Type type); |
|
||||
|
protected abstract string GetRuleName(Type type); |
||||
protected abstract string GetRuleName(Type type); |
|
||||
|
protected virtual async Task<WorkflowRules[]> GetFileSystemRulesAsync(Type type, CancellationToken cancellationToken = default) |
||||
protected virtual async Task<WorkflowRules[]> GetFileSystemRulesAsync(Type type, CancellationToken cancellationToken = default) |
{ |
||||
{ |
var ruleId = GetRuleId(type); |
||||
var ruleId = GetRuleId(type); |
var ruleFile = GetRuleName(type); |
||||
var ruleFile = GetRuleName(type); |
var fileInfo = FileProvider.GetFileInfo(ruleFile); |
||||
var fileInfo = FileProvider.GetFileInfo(ruleFile); |
if (fileInfo != null && fileInfo.Exists) |
||||
if (fileInfo != null && fileInfo.Exists) |
{ |
||||
{ |
// 规则文件监控
|
||||
// 规则文件监控
|
ChangeToken.OnChange( |
||||
// TODO: 删除模块的规则缓存还需要删除RulesEngine中rulesCache已编译的规则缓存
|
() => FileProvider.Watch(ruleFile), |
||||
ChangeToken.OnChange( |
(int ruleId) => |
||||
() => FileProvider.Watch(ruleFile), |
{ |
||||
(int ruleId) => |
// 清除规则缓存
|
||||
{ |
RulesCache.Remove(ruleId); |
||||
RulesCache.Remove(ruleId); |
}, ruleId); |
||||
}, ruleId); |
|
||||
|
// 打开文本流
|
||||
// 打开文本流
|
using (var stream = fileInfo.CreateReadStream()) |
||||
using (var stream = fileInfo.CreateReadStream()) |
{ |
||||
{ |
var result = new byte[stream.Length]; |
||||
var result = new byte[stream.Length]; |
await stream.ReadAsync(result, 0, (int)stream.Length); |
||||
await stream.ReadAsync(result, 0, (int)stream.Length); |
var ruleDsl = Encoding.UTF8.GetString(result); |
||||
var ruleDsl = Encoding.UTF8.GetString(result); |
// 解析
|
||||
// 解析
|
return JsonSerializer.Deserialize<WorkflowRules[]>(ruleDsl); |
||||
return JsonSerializer.Deserialize<WorkflowRules[]>(ruleDsl); |
} |
||||
} |
} |
||||
} |
return new WorkflowRules[0]; |
||||
return new WorkflowRules[0]; |
} |
||||
} |
} |
||||
} |
} |
||||
} |
|
||||
|
|||||
20
aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN/Abp/Rules/RulesEngine/FileProviders/Physical/AbpRulesEnginePthsicalFileResolveOptions.cs → aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN/Abp/Rules/RulesEngine/FileProviders/Physical/AbpRulesEnginePhysicalFileResolveOptions.cs
20
aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN/Abp/Rules/RulesEngine/FileProviders/Physical/AbpRulesEnginePthsicalFileResolveOptions.cs → aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN/Abp/Rules/RulesEngine/FileProviders/Physical/AbpRulesEnginePhysicalFileResolveOptions.cs
@ -1,10 +1,10 @@ |
|||||
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical |
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical |
||||
{ |
{ |
||||
public class AbpRulesEnginePthsicalFileResolveOptions |
public class AbpRulesEnginePhysicalFileResolveOptions |
||||
{ |
{ |
||||
/// <summary>
|
/// <summary>
|
||||
/// 本地文件路径
|
/// 本地文件路径
|
||||
/// </summary>
|
/// </summary>
|
||||
public string PhysicalPath { get; set; } |
public string PhysicalPath { get; set; } |
||||
} |
} |
||||
} |
} |
||||
@ -1,44 +1,44 @@ |
|||||
using Microsoft.Extensions.DependencyInjection; |
using Microsoft.Extensions.DependencyInjection; |
||||
using Microsoft.Extensions.FileProviders; |
using Microsoft.Extensions.FileProviders; |
||||
using Microsoft.Extensions.Options; |
using Microsoft.Extensions.Options; |
||||
using System; |
using System; |
||||
using System.IO; |
using System.IO; |
||||
using Volo.Abp.DependencyInjection; |
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical |
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical |
||||
{ |
{ |
||||
public class PhysicalFileWorkflowRulesResolveContributor : FileProviderWorkflowRulesResolveContributor, ISingletonDependency |
public class PhysicalFileWorkflowRulesResolveContributor : FileProviderWorkflowRulesResolveContributor, ISingletonDependency |
||||
{ |
{ |
||||
public override string Name => "PhysicalFile"; |
public override string Name => "PhysicalFile"; |
||||
|
|
||||
private RuleIdGenerator _ruleIdGenerator; |
private RuleIdGenerator _ruleIdGenerator; |
||||
private AbpRulesEngineOptions _rulesEngineOptions; |
private AbpRulesEngineOptions _rulesEngineOptions; |
||||
private AbpRulesEnginePthsicalFileResolveOptions _fileResolveOptions; |
private AbpRulesEnginePhysicalFileResolveOptions _fileResolveOptions; |
||||
|
|
||||
public PhysicalFileWorkflowRulesResolveContributor() |
public PhysicalFileWorkflowRulesResolveContributor() |
||||
{ |
{ |
||||
} |
} |
||||
|
|
||||
protected override void Initialize(IServiceProvider serviceProvider) |
protected override void Initialize(IServiceProvider serviceProvider) |
||||
{ |
{ |
||||
_ruleIdGenerator = serviceProvider.GetRequiredService<RuleIdGenerator>(); |
_ruleIdGenerator = serviceProvider.GetRequiredService<RuleIdGenerator>(); |
||||
_rulesEngineOptions = serviceProvider.GetRequiredService<IOptions<AbpRulesEngineOptions>>().Value; |
_rulesEngineOptions = serviceProvider.GetRequiredService<IOptions<AbpRulesEngineOptions>>().Value; |
||||
_fileResolveOptions = serviceProvider.GetRequiredService<IOptions<AbpRulesEnginePthsicalFileResolveOptions>>().Value; |
_fileResolveOptions = serviceProvider.GetRequiredService<IOptions<AbpRulesEnginePhysicalFileResolveOptions>>().Value; |
||||
} |
} |
||||
|
|
||||
protected override IFileProvider BuildFileProvider(RulesInitializationContext context) |
protected override IFileProvider BuildFileProvider(RulesInitializationContext context) |
||||
{ |
{ |
||||
// 未指定路径不启用
|
// 未指定路径不启用
|
||||
if (!_fileResolveOptions.PhysicalPath.IsNullOrWhiteSpace() && |
if (!_fileResolveOptions.PhysicalPath.IsNullOrWhiteSpace() && |
||||
Directory.Exists(_fileResolveOptions.PhysicalPath)) |
Directory.Exists(_fileResolveOptions.PhysicalPath)) |
||||
{ |
{ |
||||
return new PhysicalFileProvider(_fileResolveOptions.PhysicalPath); |
return new PhysicalFileProvider(_fileResolveOptions.PhysicalPath); |
||||
} |
} |
||||
return null; |
return null; |
||||
} |
} |
||||
|
|
||||
protected override int GetRuleId(Type type) => _ruleIdGenerator.CreateRuleId(type, _rulesEngineOptions.IgnoreMultiTenancy); |
protected override int GetRuleId(Type type) => _ruleIdGenerator.CreateRuleId(type, _rulesEngineOptions.IgnoreMultiTenancy); |
||||
|
|
||||
protected override string GetRuleName(Type type) => $"{_ruleIdGenerator.CreateRuleName(type, _rulesEngineOptions.IgnoreMultiTenancy)}.json"; |
protected override string GetRuleName(Type type) => $"{_ruleIdGenerator.CreateRuleName(type, _rulesEngineOptions.IgnoreMultiTenancy)}.json"; |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,82 +1,81 @@ |
|||||
using RulesEngine; |
using Microsoft.Extensions.Options; |
||||
using RulesEngine.Interfaces; |
using RulesEngine; |
||||
using RulesEngine.Models; |
using RulesEngine.Interfaces; |
||||
using System.Collections.Generic; |
using RulesEngine.Models; |
||||
using System.Linq; |
using System.Collections.Generic; |
||||
using System.Threading; |
using System.Linq; |
||||
using System.Threading.Tasks; |
using System.Threading; |
||||
using Volo.Abp.DependencyInjection; |
using System.Threading.Tasks; |
||||
using Engine = RulesEngine.RulesEngine; |
using Volo.Abp.DependencyInjection; |
||||
|
using Engine = RulesEngine.RulesEngine; |
||||
namespace LINGYUN.Abp.Rules.RulesEngine |
|
||||
{ |
namespace LINGYUN.Abp.Rules.RulesEngine |
||||
public class RulesEngineContributor : RuleContributorBase, ISingletonDependency |
{ |
||||
{ |
public class RulesEngineContributor : RuleContributorBase, ISingletonDependency |
||||
private IRulesEngine _ruleEngine; |
{ |
||||
private readonly IWorkflowRulesResolver _workflowRulesResolver; |
private IRulesEngine _ruleEngine; |
||||
|
private readonly AbpRulesEngineOptions _options; |
||||
public RulesEngineContributor( |
private readonly IWorkflowRulesResolver _workflowRulesResolver; |
||||
IWorkflowRulesResolver workflowRulesResolver) |
|
||||
{ |
public RulesEngineContributor( |
||||
_workflowRulesResolver = workflowRulesResolver; |
IWorkflowRulesResolver workflowRulesResolver, |
||||
} |
IOptions<AbpRulesEngineOptions> options) |
||||
|
{ |
||||
public override void Initialize(RulesInitializationContext context) |
_options = options.Value; |
||||
{ |
_workflowRulesResolver = workflowRulesResolver; |
||||
_ruleEngine = CreateRulesEngine(); |
} |
||||
|
|
||||
_workflowRulesResolver.Initialize(context); |
public override void Initialize(RulesInitializationContext context) |
||||
} |
{ |
||||
|
_ruleEngine = CreateRulesEngine(); |
||||
public override async Task ExecuteAsync<T>(T input, object[] @params = null, CancellationToken cancellationToken = default) |
_workflowRulesResolver.Initialize(context); |
||||
{ |
} |
||||
var result = await _workflowRulesResolver.ResolveWorkflowRulesAsync(typeof(T)); |
|
||||
|
public override async Task ExecuteAsync<T>(T input, object[] @params = null, CancellationToken cancellationToken = default) |
||||
if (result.WorkflowRules.Any()) |
{ |
||||
{ |
var result = await _workflowRulesResolver.ResolveWorkflowRulesAsync(typeof(T)); |
||||
await ExecuteRulesAsync(input, result.WorkflowRules.ToArray(), @params); |
|
||||
} |
if (result.WorkflowRules.Any()) |
||||
} |
{ |
||||
|
await ExecuteRulesAsync(input, result.WorkflowRules.ToArray(), @params); |
||||
public override void Shutdown() |
} |
||||
{ |
} |
||||
} |
|
||||
/// <summary>
|
public override void Shutdown() |
||||
/// 重写自行构建规则引擎
|
{ |
||||
/// </summary>
|
} |
||||
/// <returns></returns>
|
/// <summary>
|
||||
protected virtual Engine CreateRulesEngine() |
/// 重写自行构建规则引擎
|
||||
{ |
/// </summary>
|
||||
var reSetting = new ReSettings |
/// <returns></returns>
|
||||
{ |
protected virtual Engine CreateRulesEngine() |
||||
NestedRuleExecutionMode = NestedRuleExecutionMode.Performance |
{ |
||||
}; |
return new Engine(Logger, _options.Settings); |
||||
|
} |
||||
return new Engine(Logger, reSetting); |
|
||||
} |
protected virtual async Task ExecuteRulesAsync<T>(T input, WorkflowRules[] workflowRules, object[] @params = null) |
||||
|
{ |
||||
protected virtual async Task ExecuteRulesAsync<T>(T input, WorkflowRules[] workflowRules, object[] @params = null) |
// TODO: 性能缺陷 规则文件每一次调用都会重复编译
|
||||
{ |
_ruleEngine.AddOrUpdateWorkflow(workflowRules); |
||||
_ruleEngine.AddWorkflow(workflowRules); |
|
||||
|
// 传入参与验证的实体参数
|
||||
// 传入参与验证的实体参数
|
var inputs = new List<object>() |
||||
var inputs = new List<object>() |
{ |
||||
{ |
input |
||||
input |
}; |
||||
}; |
if (@params != null && @params.Any()) |
||||
if (@params != null && @params.Any()) |
{ |
||||
{ |
inputs.AddRange(@params); |
||||
inputs.AddRange(@params); |
} |
||||
} |
// 其他参数以此类推
|
||||
// 其他参数以此类推
|
|
||||
|
foreach (var workflowRule in workflowRules) |
||||
foreach (var workflowRule in workflowRules) |
{ |
||||
{ |
// 执行当前的规则
|
||||
// 执行当前的规则
|
var ruleResult = await _ruleEngine.ExecuteAllRulesAsync(workflowRule.WorkflowName, inputs.ToArray()); |
||||
var ruleResult = await _ruleEngine.ExecuteAllRulesAsync(workflowRule.WorkflowName, inputs.ToArray()); |
// 用户自定义扩展方法,规则校验错误抛出异常
|
||||
// 用户自定义扩展方法,规则校验错误抛出异常
|
ruleResult.ThrowOfFaildExecute(); |
||||
ruleResult.ThrowOfFaildExecute(); |
} |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
} |
|
||||
|
|||||
@ -1,54 +1,54 @@ |
|||||
# LINGYUN.Abp.Rules.RulesEngine |
# LINGYUN.Abp.Rules.RulesEngine |
||||
|
|
||||
## 模块说明 |
## 模块说明 |
||||
|
|
||||
集成微软规则引擎的实现 |
集成微软规则引擎的实现 |
||||
|
|
||||
默认实现一个本地文件系统规则提供者,根据用户配置的 **AbpRulesEnginePthsicalFileResolveOptions.PhysicalPath** 路径检索规则文件 |
默认实现一个本地文件系统规则提供者,根据用户配置的 **AbpRulesEnginePthsicalFileResolveOptions.PhysicalPath** 路径检索规则文件 |
||||
|
|
||||
文件名如下: |
文件名如下: |
||||
|
|
||||
PhysicalPath/CurrentTenant.Id[如果存在]/验证规则实体类型名称[typeof(Input).Name].json |
PhysicalPath/CurrentTenant.Id[如果存在]/验证规则实体类型名称[typeof(Input).Name].json |
||||
|
|
||||
自定义的规则提供者需要实现 **IWorkflowRulesResolveContributor** 接口,可能不需要实现初始化与释放资源,因此提供了一个抽象的 **WorkflowRulesResolveContributorBase** |
自定义的规则提供者需要实现 **IWorkflowRulesResolveContributor** 接口,可能不需要实现初始化与释放资源,因此提供了一个抽象的 **WorkflowRulesResolveContributorBase** |
||||
|
|
||||
并添加到 **AbpRulesEngineResolveOptions.WorkflowRulesResolvers** 中 |
并添加到 **AbpRulesEngineResolveOptions.WorkflowRulesResolvers** 中 |
||||
|
|
||||
### 基础模块 |
### 基础模块 |
||||
|
|
||||
### 高阶模块 |
### 高阶模块 |
||||
|
|
||||
### 权限定义 |
### 权限定义 |
||||
|
|
||||
### 功能定义 |
### 功能定义 |
||||
|
|
||||
### 配置定义 |
### 配置定义 |
||||
|
|
||||
### 如何使用 |
### 如何使用 |
||||
|
|
||||
|
|
||||
```csharp |
```csharp |
||||
|
|
||||
[DependsOn( |
[DependsOn( |
||||
typeof(AbpRulesEngineModule))] |
typeof(AbpRulesEngineModule))] |
||||
public class YouProjectModule : AbpModule |
public class YouProjectModule : AbpModule |
||||
{ |
{ |
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
{ |
{ |
||||
Configure<AbpRulesEngineResolveOptions>(options => |
Configure<AbpRulesEngineResolveOptions>(options => |
||||
{ |
{ |
||||
// 添加自行实现的规则解析提供者 |
// 添加自行实现的规则解析提供者 |
||||
options.WorkflowRulesResolvers.Add(new FakeWorkflowRulesResolveContributor()); |
options.WorkflowRulesResolvers.Add(new FakeWorkflowRulesResolveContributor()); |
||||
}); |
}); |
||||
|
|
||||
Configure<AbpRulesEnginePthsicalFileResolveOptions>(options => |
Configure<AbpRulesEnginePhysicalFileResolveOptions>(options => |
||||
{ |
{ |
||||
// 指定真实存在的本地文件路径, 否则将不会检索本地规则文件 |
// 指定真实存在的本地文件路径, 否则将不会检索本地规则文件 |
||||
options.PhysicalPath = Path.Combine(Directory.GetCurrentDirectory(), "Rules"); |
options.PhysicalPath = Path.Combine(Directory.GetCurrentDirectory(), "Rules"); |
||||
}); |
}); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
``` |
``` |
||||
|
|
||||
### 更新日志 |
### 更新日志 |
||||
|
|||||
@ -1,16 +1,20 @@ |
|||||
using System; |
using System; |
||||
|
using Volo.Abp.Data; |
||||
namespace LINGYUN.Abp.Rules |
|
||||
{ |
namespace LINGYUN.Abp.Rules |
||||
public class RulesInitializationContext : IServiceProvider |
{ |
||||
{ |
public class RulesInitializationContext : IServiceProvider, IHasExtraProperties |
||||
public IServiceProvider ServiceProvider { get; } |
{ |
||||
|
public IServiceProvider ServiceProvider { get; } |
||||
internal RulesInitializationContext(IServiceProvider serviceProvider) |
|
||||
{ |
public ExtraPropertyDictionary ExtraProperties { get; } |
||||
ServiceProvider = serviceProvider; |
|
||||
} |
internal RulesInitializationContext(IServiceProvider serviceProvider) |
||||
|
{ |
||||
public object GetService(Type serviceType) => ServiceProvider.GetService(serviceType); |
ServiceProvider = serviceProvider; |
||||
} |
ExtraProperties = new ExtraPropertyDictionary(); |
||||
} |
} |
||||
|
|
||||
|
public object GetService(Type serviceType) => ServiceProvider.GetService(serviceType); |
||||
|
} |
||||
|
} |
||||
|
|||||
@ -1,21 +1,21 @@ |
|||||
using LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical; |
using LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical; |
||||
using LINGYUN.Abp.Tests; |
using LINGYUN.Abp.Tests; |
||||
using System.IO; |
using System.IO; |
||||
using Volo.Abp.Modularity; |
using Volo.Abp.Modularity; |
||||
|
|
||||
namespace LINGYUN.Abp.Rules.RulesEngine |
namespace LINGYUN.Abp.Rules.RulesEngine |
||||
{ |
{ |
||||
[DependsOn( |
[DependsOn( |
||||
typeof(AbpRulesEngineModule), |
typeof(AbpRulesEngineModule), |
||||
typeof(AbpTestsBaseModule))] |
typeof(AbpTestsBaseModule))] |
||||
public class AbpRulesEngineTestModule : AbpModule |
public class AbpRulesEngineTestModule : AbpModule |
||||
{ |
{ |
||||
public override void ConfigureServices(ServiceConfigurationContext context) |
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
{ |
{ |
||||
Configure<AbpRulesEnginePthsicalFileResolveOptions>(options => |
Configure<AbpRulesEnginePhysicalFileResolveOptions>(options => |
||||
{ |
{ |
||||
options.PhysicalPath = Path.Combine(Directory.GetCurrentDirectory(), "Rules"); |
options.PhysicalPath = Path.Combine(Directory.GetCurrentDirectory(), "Rules"); |
||||
}); |
}); |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
|
|||||
@ -1,117 +1,118 @@ |
|||||
using Shouldly; |
using Shouldly; |
||||
using System.Threading.Tasks; |
using System; |
||||
using Volo.Abp.Validation; |
using System.Threading.Tasks; |
||||
using Xunit; |
using Volo.Abp.Validation; |
||||
|
using Xunit; |
||||
namespace LINGYUN.Abp.Rules.RulesEngine |
|
||||
{ |
namespace LINGYUN.Abp.Rules.RulesEngine |
||||
public class TestInputRuleTests : AbpRulesEngineTestBase |
{ |
||||
{ |
public class TestInputRuleTests : AbpRulesEngineTestBase |
||||
private readonly IRuleProvider _ruleProvider; |
{ |
||||
|
private readonly IRuleProvider _ruleProvider; |
||||
public TestInputRuleTests() |
|
||||
{ |
public TestInputRuleTests() |
||||
_ruleProvider = GetRequiredService<IRuleProvider>(); |
{ |
||||
} |
_ruleProvider = GetRequiredService<IRuleProvider>(); |
||||
|
} |
||||
[Fact] |
|
||||
public async Task Input_Required_Should_Required() |
[Fact] |
||||
{ |
public async Task Input_Required_Should_Required() |
||||
var input = new TestInput |
{ |
||||
{ |
var input = new TestInput |
||||
Integer1 = 101, |
{ |
||||
Integer2 = 99, |
Integer1 = 101, |
||||
Length = "123456" |
Integer2 = 99, |
||||
} |
Length = "123456" |
||||
; |
} |
||||
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () => |
; |
||||
{ |
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () => |
||||
await _ruleProvider.ExecuteAsync(input); |
{ |
||||
}); |
await _ruleProvider.ExecuteAsync(input); |
||||
|
}); |
||||
exception.Message.ShouldBe("一个或多个规则未通过"); |
|
||||
exception.ValidationErrors.Count.ShouldBe(1); |
exception.Message.ShouldBe("一个或多个规则未通过"); |
||||
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Required 必须输入!"); |
exception.ValidationErrors.Count.ShouldBe(1); |
||||
} |
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Required 必须输入!"); |
||||
|
} |
||||
[Fact] |
|
||||
public async Task Input_Integer1_Should_MustBeGreaterThan100() |
[Fact] |
||||
{ |
public async Task Input_Integer1_Should_MustBeGreaterThan100() |
||||
var input = new TestInput |
{ |
||||
{ |
var input = new TestInput |
||||
Required = "123456", |
{ |
||||
Integer1 = 99, |
Required = "123456", |
||||
Integer2 = 99, |
Integer1 = 99, |
||||
Length = "123456" |
Integer2 = 99, |
||||
}; |
Length = "123456" |
||||
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () => |
}; |
||||
{ |
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () => |
||||
await _ruleProvider.ExecuteAsync(input); |
{ |
||||
}); |
await _ruleProvider.ExecuteAsync(input); |
||||
|
}); |
||||
exception.Message.ShouldBe("一个或多个规则未通过"); |
|
||||
exception.ValidationErrors.Count.ShouldBe(1); |
exception.Message.ShouldBe("一个或多个规则未通过"); |
||||
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Integer1 必须大于100!"); |
exception.ValidationErrors.Count.ShouldBe(1); |
||||
} |
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Integer1 必须大于100!"); |
||||
|
} |
||||
[Fact] |
|
||||
public async Task Input_Integer2_Should_MustBeLessThan100() |
[Fact] |
||||
{ |
public async Task Input_Integer2_Should_MustBeLessThan100() |
||||
var input = new TestInput |
{ |
||||
{ |
var input = new TestInput |
||||
Required = "123456", |
{ |
||||
Integer1 = 101, |
Required = "123456", |
||||
Integer2 = 100, |
Integer1 = 101, |
||||
Length = "123456" |
Integer2 = 100, |
||||
}; |
Length = "123456" |
||||
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () => |
}; |
||||
{ |
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () => |
||||
await _ruleProvider.ExecuteAsync(input); |
{ |
||||
}); |
await _ruleProvider.ExecuteAsync(input); |
||||
|
}); |
||||
exception.Message.ShouldBe("一个或多个规则未通过"); |
|
||||
exception.ValidationErrors.Count.ShouldBe(1); |
exception.Message.ShouldBe("一个或多个规则未通过"); |
||||
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Integer2 必须小于100!"); |
exception.ValidationErrors.Count.ShouldBe(1); |
||||
} |
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Integer2 必须小于100!"); |
||||
|
} |
||||
[Fact] |
|
||||
public async Task Input_Sum_Integer1_And_Integer2_Should_MustBeGreaterThan150() |
[Fact] |
||||
{ |
public async Task Input_Sum_Integer1_And_Integer2_Should_MustBeGreaterThan150() |
||||
var input = new TestInput |
{ |
||||
{ |
var input = new TestInput |
||||
Required = "1", |
{ |
||||
Integer1 = 101, |
Required = "1", |
||||
Integer2 = 48, |
Integer1 = 101, |
||||
Length = "123456" |
Integer2 = 48, |
||||
}; |
Length = "123456" |
||||
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () => |
}; |
||||
{ |
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () => |
||||
await _ruleProvider.ExecuteAsync(input); |
{ |
||||
}); |
await _ruleProvider.ExecuteAsync(input); |
||||
|
}); |
||||
exception.Message.ShouldBe("一个或多个规则未通过"); |
|
||||
exception.ValidationErrors.Count.ShouldBe(1); |
exception.Message.ShouldBe("一个或多个规则未通过"); |
||||
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Integer1 与 Integer2 之和 必须大于150!"); |
exception.ValidationErrors.Count.ShouldBe(1); |
||||
} |
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Integer1 与 Integer2 之和 必须大于150!"); |
||||
|
} |
||||
[Fact] |
|
||||
public async Task Input_Required_Length_Should_MustBeGreaterThan5() |
[Fact] |
||||
{ |
public async Task Input_Required_Length_Should_MustBeGreaterThan5() |
||||
var input = new TestInput |
{ |
||||
{ |
var input = new TestInput |
||||
Required = "1", |
{ |
||||
Integer1 = 101, |
Required = "1", |
||||
Integer2 = 50, |
Integer1 = 101, |
||||
Length = "12345" |
Integer2 = 50, |
||||
}; |
Length = "12345" |
||||
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () => |
}; |
||||
{ |
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () => |
||||
await _ruleProvider.ExecuteAsync(input); |
{ |
||||
}); |
await _ruleProvider.ExecuteAsync(input); |
||||
|
}); |
||||
exception.Message.ShouldBe("一个或多个规则未通过"); |
|
||||
exception.ValidationErrors.Count.ShouldBe(1); |
exception.Message.ShouldBe("一个或多个规则未通过"); |
||||
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Length 长度必须大于5!"); |
exception.ValidationErrors.Count.ShouldBe(1); |
||||
} |
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Length 长度必须大于5!"); |
||||
} |
} |
||||
} |
} |
||||
|
} |
||||
|
|||||
Loading…
Reference in new issue