Browse Source

upgrade RulesEngine

pull/294/head
cKey 4 years ago
parent
commit
47a3add772
  1. 2
      aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN.Abp.Rules.RulesEngine.csproj
  2. 40
      aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN/Abp/Rules/RulesEngine/AbpRulesEngineOptions.cs
  3. 209
      aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN/Abp/Rules/RulesEngine/FileProviders/FileProviderWorkflowRulesResolveContributor.cs
  4. 20
      aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN/Abp/Rules/RulesEngine/FileProviders/Physical/AbpRulesEnginePhysicalFileResolveOptions.cs
  5. 88
      aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN/Abp/Rules/RulesEngine/FileProviders/Physical/PhysicalFileWorkflowRulesResolveContributor.cs
  6. 163
      aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN/Abp/Rules/RulesEngine/RulesEngineContributor.cs
  7. 108
      aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/README.md
  8. 36
      aspnet-core/modules/rules/LINGYUN.Abp.Rules/LINGYUN/Abp/Rules/RulesInitializationContext.cs
  9. 42
      aspnet-core/tests/LINGYUN.Abp.Rules.RulesEngine.Tests/LINGYUN/Abp/Rules/RulesEngine/AbpRulesEngineTestModule.cs
  10. 235
      aspnet-core/tests/LINGYUN.Abp.Rules.RulesEngine.Tests/LINGYUN/Abp/Rules/RulesEngine/TestInputRuleTests.cs

2
aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN.Abp.Rules.RulesEngine.csproj

@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="RulesEngine" Version="3.1.0" />
<PackageReference Include="RulesEngine" Version="3.3.0" />
<PackageReference Include="Volo.Abp.Json" Version="4.4.0" />
</ItemGroup>

40
aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN/Abp/Rules/RulesEngine/AbpRulesEngineOptions.cs

@ -1,16 +1,24 @@
using Volo.Abp.Collections;
namespace LINGYUN.Abp.Rules.RulesEngine
{
public class AbpRulesEngineOptions
{
/// <summary>
/// 是否忽略租户
/// </summary>
public bool IgnoreMultiTenancy { get; set; }
public AbpRulesEngineOptions()
{
}
}
}
using RulesEngine.Models;
namespace LINGYUN.Abp.Rules.RulesEngine
{
public class AbpRulesEngineOptions
{
/// <summary>
/// 是否忽略租户
/// </summary>
public bool IgnoreMultiTenancy { get; set; }
/// <summary>
/// 规则引擎可配置
/// </summary>
public ReSettings Settings { get; set; }
public AbpRulesEngineOptions()
{
Settings = new ReSettings
{
NestedRuleExecutionMode = NestedRuleExecutionMode.Performance
};
}
}
}

209
aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN/Abp/Rules/RulesEngine/FileProviders/FileProviderWorkflowRulesResolveContributor.cs

@ -1,105 +1,104 @@
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
using RulesEngine.Models;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Json;
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders
{
public abstract class FileProviderWorkflowRulesResolveContributor : WorkflowRulesResolveContributorBase
{
protected IMemoryCache RulesCache { get; private set; }
protected IJsonSerializer JsonSerializer { get; private set; }
protected IFileProvider FileProvider { get; private set; }
protected FileProviderWorkflowRulesResolveContributor()
{
}
public override void Initialize(RulesInitializationContext context)
{
Initialize(context.ServiceProvider);
RulesCache = context.ServiceProvider.GetRequiredService<IMemoryCache>();
JsonSerializer = context.ServiceProvider.GetRequiredService<IJsonSerializer>();
FileProvider = BuildFileProvider(context);
}
protected virtual void Initialize(IServiceProvider serviceProvider)
{
}
protected abstract IFileProvider BuildFileProvider(RulesInitializationContext context);
public override async Task ResolveAsync(IWorkflowRulesResolveContext context)
{
if (FileProvider != null)
{
context.WorkflowRules = await GetCachedRulesAsync(context.Type);
}
context.Handled = true;
}
public override void Shutdown()
{
if (FileProvider != null && FileProvider is IDisposable resource)
{
resource.Dispose();
}
}
private async Task<WorkflowRules[]> GetCachedRulesAsync(Type type, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
var ruleId = GetRuleId(type);
return await RulesCache.GetOrCreateAsync(ruleId,
async (entry) =>
{
entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(30));
return await GetFileSystemRulesAsync(type, cancellationToken);
});
}
protected abstract int GetRuleId(Type type);
protected abstract string GetRuleName(Type type);
protected virtual async Task<WorkflowRules[]> GetFileSystemRulesAsync(Type type, CancellationToken cancellationToken = default)
{
var ruleId = GetRuleId(type);
var ruleFile = GetRuleName(type);
var fileInfo = FileProvider.GetFileInfo(ruleFile);
if (fileInfo != null && fileInfo.Exists)
{
// 规则文件监控
// TODO: 删除模块的规则缓存还需要删除RulesEngine中rulesCache已编译的规则缓存
ChangeToken.OnChange(
() => FileProvider.Watch(ruleFile),
(int ruleId) =>
{
RulesCache.Remove(ruleId);
}, ruleId);
// 打开文本流
using (var stream = fileInfo.CreateReadStream())
{
var result = new byte[stream.Length];
await stream.ReadAsync(result, 0, (int)stream.Length);
var ruleDsl = Encoding.UTF8.GetString(result);
// 解析
return JsonSerializer.Deserialize<WorkflowRules[]>(ruleDsl);
}
}
return new WorkflowRules[0];
}
}
}
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Primitives;
using RulesEngine.Models;
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Json;
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders
{
public abstract class FileProviderWorkflowRulesResolveContributor : WorkflowRulesResolveContributorBase
{
protected IMemoryCache RulesCache { get; private set; }
protected IJsonSerializer JsonSerializer { get; private set; }
protected IFileProvider FileProvider { get; private set; }
protected FileProviderWorkflowRulesResolveContributor()
{
}
public override void Initialize(RulesInitializationContext context)
{
Initialize(context.ServiceProvider);
RulesCache = context.GetRequiredService<IMemoryCache>();
JsonSerializer = context.GetRequiredService<IJsonSerializer>();
FileProvider = BuildFileProvider(context);
}
protected virtual void Initialize(IServiceProvider serviceProvider)
{
}
protected abstract IFileProvider BuildFileProvider(RulesInitializationContext context);
public override async Task ResolveAsync(IWorkflowRulesResolveContext context)
{
if (FileProvider != null)
{
context.WorkflowRules = await GetCachedRulesAsync(context.Type);
}
context.Handled = true;
}
public override void Shutdown()
{
if (FileProvider != null && FileProvider is IDisposable resource)
{
resource.Dispose();
}
}
private async Task<WorkflowRules[]> GetCachedRulesAsync(Type type, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
var ruleId = GetRuleId(type);
return await RulesCache.GetOrCreateAsync(ruleId,
async (entry) =>
{
entry.SetAbsoluteExpiration(TimeSpan.FromMinutes(30));
return await GetFileSystemRulesAsync(type, cancellationToken);
});
}
protected abstract int GetRuleId(Type type);
protected abstract string GetRuleName(Type type);
protected virtual async Task<WorkflowRules[]> GetFileSystemRulesAsync(Type type, CancellationToken cancellationToken = default)
{
var ruleId = GetRuleId(type);
var ruleFile = GetRuleName(type);
var fileInfo = FileProvider.GetFileInfo(ruleFile);
if (fileInfo != null && fileInfo.Exists)
{
// 规则文件监控
ChangeToken.OnChange(
() => FileProvider.Watch(ruleFile),
(int ruleId) =>
{
// 清除规则缓存
RulesCache.Remove(ruleId);
}, ruleId);
// 打开文本流
using (var stream = fileInfo.CreateReadStream())
{
var result = new byte[stream.Length];
await stream.ReadAsync(result, 0, (int)stream.Length);
var ruleDsl = Encoding.UTF8.GetString(result);
// 解析
return JsonSerializer.Deserialize<WorkflowRules[]>(ruleDsl);
}
}
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

@ -1,10 +1,10 @@
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical
{
public class AbpRulesEnginePthsicalFileResolveOptions
{
/// <summary>
/// 本地文件路径
/// </summary>
public string PhysicalPath { get; set; }
}
}
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical
{
public class AbpRulesEnginePhysicalFileResolveOptions
{
/// <summary>
/// 本地文件路径
/// </summary>
public string PhysicalPath { get; set; }
}
}

88
aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN/Abp/Rules/RulesEngine/FileProviders/Physical/PhysicalFileWorkflowRulesResolveContributor.cs

@ -1,44 +1,44 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;
using System;
using System.IO;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical
{
public class PhysicalFileWorkflowRulesResolveContributor : FileProviderWorkflowRulesResolveContributor, ISingletonDependency
{
public override string Name => "PhysicalFile";
private RuleIdGenerator _ruleIdGenerator;
private AbpRulesEngineOptions _rulesEngineOptions;
private AbpRulesEnginePthsicalFileResolveOptions _fileResolveOptions;
public PhysicalFileWorkflowRulesResolveContributor()
{
}
protected override void Initialize(IServiceProvider serviceProvider)
{
_ruleIdGenerator = serviceProvider.GetRequiredService<RuleIdGenerator>();
_rulesEngineOptions = serviceProvider.GetRequiredService<IOptions<AbpRulesEngineOptions>>().Value;
_fileResolveOptions = serviceProvider.GetRequiredService<IOptions<AbpRulesEnginePthsicalFileResolveOptions>>().Value;
}
protected override IFileProvider BuildFileProvider(RulesInitializationContext context)
{
// 未指定路径不启用
if (!_fileResolveOptions.PhysicalPath.IsNullOrWhiteSpace() &&
Directory.Exists(_fileResolveOptions.PhysicalPath))
{
return new PhysicalFileProvider(_fileResolveOptions.PhysicalPath);
}
return null;
}
protected override int GetRuleId(Type type) => _ruleIdGenerator.CreateRuleId(type, _rulesEngineOptions.IgnoreMultiTenancy);
protected override string GetRuleName(Type type) => $"{_ruleIdGenerator.CreateRuleName(type, _rulesEngineOptions.IgnoreMultiTenancy)}.json";
}
}
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;
using System;
using System.IO;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical
{
public class PhysicalFileWorkflowRulesResolveContributor : FileProviderWorkflowRulesResolveContributor, ISingletonDependency
{
public override string Name => "PhysicalFile";
private RuleIdGenerator _ruleIdGenerator;
private AbpRulesEngineOptions _rulesEngineOptions;
private AbpRulesEnginePhysicalFileResolveOptions _fileResolveOptions;
public PhysicalFileWorkflowRulesResolveContributor()
{
}
protected override void Initialize(IServiceProvider serviceProvider)
{
_ruleIdGenerator = serviceProvider.GetRequiredService<RuleIdGenerator>();
_rulesEngineOptions = serviceProvider.GetRequiredService<IOptions<AbpRulesEngineOptions>>().Value;
_fileResolveOptions = serviceProvider.GetRequiredService<IOptions<AbpRulesEnginePhysicalFileResolveOptions>>().Value;
}
protected override IFileProvider BuildFileProvider(RulesInitializationContext context)
{
// 未指定路径不启用
if (!_fileResolveOptions.PhysicalPath.IsNullOrWhiteSpace() &&
Directory.Exists(_fileResolveOptions.PhysicalPath))
{
return new PhysicalFileProvider(_fileResolveOptions.PhysicalPath);
}
return null;
}
protected override int GetRuleId(Type type) => _ruleIdGenerator.CreateRuleId(type, _rulesEngineOptions.IgnoreMultiTenancy);
protected override string GetRuleName(Type type) => $"{_ruleIdGenerator.CreateRuleName(type, _rulesEngineOptions.IgnoreMultiTenancy)}.json";
}
}

163
aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/LINGYUN/Abp/Rules/RulesEngine/RulesEngineContributor.cs

@ -1,82 +1,81 @@
using RulesEngine;
using RulesEngine.Interfaces;
using RulesEngine.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Engine = RulesEngine.RulesEngine;
namespace LINGYUN.Abp.Rules.RulesEngine
{
public class RulesEngineContributor : RuleContributorBase, ISingletonDependency
{
private IRulesEngine _ruleEngine;
private readonly IWorkflowRulesResolver _workflowRulesResolver;
public RulesEngineContributor(
IWorkflowRulesResolver workflowRulesResolver)
{
_workflowRulesResolver = workflowRulesResolver;
}
public override void Initialize(RulesInitializationContext context)
{
_ruleEngine = CreateRulesEngine();
_workflowRulesResolver.Initialize(context);
}
public override async Task ExecuteAsync<T>(T input, object[] @params = null, CancellationToken cancellationToken = default)
{
var result = await _workflowRulesResolver.ResolveWorkflowRulesAsync(typeof(T));
if (result.WorkflowRules.Any())
{
await ExecuteRulesAsync(input, result.WorkflowRules.ToArray(), @params);
}
}
public override void Shutdown()
{
}
/// <summary>
/// 重写自行构建规则引擎
/// </summary>
/// <returns></returns>
protected virtual Engine CreateRulesEngine()
{
var reSetting = new ReSettings
{
NestedRuleExecutionMode = NestedRuleExecutionMode.Performance
};
return new Engine(Logger, reSetting);
}
protected virtual async Task ExecuteRulesAsync<T>(T input, WorkflowRules[] workflowRules, object[] @params = null)
{
_ruleEngine.AddWorkflow(workflowRules);
// 传入参与验证的实体参数
var inputs = new List<object>()
{
input
};
if (@params != null && @params.Any())
{
inputs.AddRange(@params);
}
// 其他参数以此类推
foreach (var workflowRule in workflowRules)
{
// 执行当前的规则
var ruleResult = await _ruleEngine.ExecuteAllRulesAsync(workflowRule.WorkflowName, inputs.ToArray());
// 用户自定义扩展方法,规则校验错误抛出异常
ruleResult.ThrowOfFaildExecute();
}
}
}
}
using Microsoft.Extensions.Options;
using RulesEngine;
using RulesEngine.Interfaces;
using RulesEngine.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Engine = RulesEngine.RulesEngine;
namespace LINGYUN.Abp.Rules.RulesEngine
{
public class RulesEngineContributor : RuleContributorBase, ISingletonDependency
{
private IRulesEngine _ruleEngine;
private readonly AbpRulesEngineOptions _options;
private readonly IWorkflowRulesResolver _workflowRulesResolver;
public RulesEngineContributor(
IWorkflowRulesResolver workflowRulesResolver,
IOptions<AbpRulesEngineOptions> options)
{
_options = options.Value;
_workflowRulesResolver = workflowRulesResolver;
}
public override void Initialize(RulesInitializationContext context)
{
_ruleEngine = CreateRulesEngine();
_workflowRulesResolver.Initialize(context);
}
public override async Task ExecuteAsync<T>(T input, object[] @params = null, CancellationToken cancellationToken = default)
{
var result = await _workflowRulesResolver.ResolveWorkflowRulesAsync(typeof(T));
if (result.WorkflowRules.Any())
{
await ExecuteRulesAsync(input, result.WorkflowRules.ToArray(), @params);
}
}
public override void Shutdown()
{
}
/// <summary>
/// 重写自行构建规则引擎
/// </summary>
/// <returns></returns>
protected virtual Engine CreateRulesEngine()
{
return new Engine(Logger, _options.Settings);
}
protected virtual async Task ExecuteRulesAsync<T>(T input, WorkflowRules[] workflowRules, object[] @params = null)
{
// TODO: 性能缺陷 规则文件每一次调用都会重复编译
_ruleEngine.AddOrUpdateWorkflow(workflowRules);
// 传入参与验证的实体参数
var inputs = new List<object>()
{
input
};
if (@params != null && @params.Any())
{
inputs.AddRange(@params);
}
// 其他参数以此类推
foreach (var workflowRule in workflowRules)
{
// 执行当前的规则
var ruleResult = await _ruleEngine.ExecuteAllRulesAsync(workflowRule.WorkflowName, inputs.ToArray());
// 用户自定义扩展方法,规则校验错误抛出异常
ruleResult.ThrowOfFaildExecute();
}
}
}
}

108
aspnet-core/modules/rules/LINGYUN.Abp.Rules.RulesEngine/README.md

@ -1,54 +1,54 @@
# LINGYUN.Abp.Rules.RulesEngine
## 模块说明
集成微软规则引擎的实现
默认实现一个本地文件系统规则提供者,根据用户配置的 **AbpRulesEnginePthsicalFileResolveOptions.PhysicalPath** 路径检索规则文件
文件名如下:
PhysicalPath/CurrentTenant.Id[如果存在]/验证规则实体类型名称[typeof(Input).Name].json
自定义的规则提供者需要实现 **IWorkflowRulesResolveContributor** 接口,可能不需要实现初始化与释放资源,因此提供了一个抽象的 **WorkflowRulesResolveContributorBase**
并添加到 **AbpRulesEngineResolveOptions.WorkflowRulesResolvers**
### 基础模块
### 高阶模块
### 权限定义
### 功能定义
### 配置定义
### 如何使用
```csharp
[DependsOn(
typeof(AbpRulesEngineModule))]
public class YouProjectModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpRulesEngineResolveOptions>(options =>
{
// 添加自行实现的规则解析提供者
options.WorkflowRulesResolvers.Add(new FakeWorkflowRulesResolveContributor());
});
Configure<AbpRulesEnginePthsicalFileResolveOptions>(options =>
{
// 指定真实存在的本地文件路径, 否则将不会检索本地规则文件
options.PhysicalPath = Path.Combine(Directory.GetCurrentDirectory(), "Rules");
});
}
}
```
### 更新日志
# LINGYUN.Abp.Rules.RulesEngine
## 模块说明
集成微软规则引擎的实现
默认实现一个本地文件系统规则提供者,根据用户配置的 **AbpRulesEnginePthsicalFileResolveOptions.PhysicalPath** 路径检索规则文件
文件名如下:
PhysicalPath/CurrentTenant.Id[如果存在]/验证规则实体类型名称[typeof(Input).Name].json
自定义的规则提供者需要实现 **IWorkflowRulesResolveContributor** 接口,可能不需要实现初始化与释放资源,因此提供了一个抽象的 **WorkflowRulesResolveContributorBase**
并添加到 **AbpRulesEngineResolveOptions.WorkflowRulesResolvers**
### 基础模块
### 高阶模块
### 权限定义
### 功能定义
### 配置定义
### 如何使用
```csharp
[DependsOn(
typeof(AbpRulesEngineModule))]
public class YouProjectModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpRulesEngineResolveOptions>(options =>
{
// 添加自行实现的规则解析提供者
options.WorkflowRulesResolvers.Add(new FakeWorkflowRulesResolveContributor());
});
Configure<AbpRulesEnginePhysicalFileResolveOptions>(options =>
{
// 指定真实存在的本地文件路径, 否则将不会检索本地规则文件
options.PhysicalPath = Path.Combine(Directory.GetCurrentDirectory(), "Rules");
});
}
}
```
### 更新日志

36
aspnet-core/modules/rules/LINGYUN.Abp.Rules/LINGYUN/Abp/Rules/RulesInitializationContext.cs

@ -1,16 +1,20 @@
using System;
namespace LINGYUN.Abp.Rules
{
public class RulesInitializationContext : IServiceProvider
{
public IServiceProvider ServiceProvider { get; }
internal RulesInitializationContext(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public object GetService(Type serviceType) => ServiceProvider.GetService(serviceType);
}
}
using System;
using Volo.Abp.Data;
namespace LINGYUN.Abp.Rules
{
public class RulesInitializationContext : IServiceProvider, IHasExtraProperties
{
public IServiceProvider ServiceProvider { get; }
public ExtraPropertyDictionary ExtraProperties { get; }
internal RulesInitializationContext(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
ExtraProperties = new ExtraPropertyDictionary();
}
public object GetService(Type serviceType) => ServiceProvider.GetService(serviceType);
}
}

42
aspnet-core/tests/LINGYUN.Abp.Rules.RulesEngine.Tests/LINGYUN/Abp/Rules/RulesEngine/AbpRulesEngineTestModule.cs

@ -1,21 +1,21 @@
using LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical;
using LINGYUN.Abp.Tests;
using System.IO;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Rules.RulesEngine
{
[DependsOn(
typeof(AbpRulesEngineModule),
typeof(AbpTestsBaseModule))]
public class AbpRulesEngineTestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpRulesEnginePthsicalFileResolveOptions>(options =>
{
options.PhysicalPath = Path.Combine(Directory.GetCurrentDirectory(), "Rules");
});
}
}
}
using LINGYUN.Abp.Rules.RulesEngine.FileProviders.Physical;
using LINGYUN.Abp.Tests;
using System.IO;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Rules.RulesEngine
{
[DependsOn(
typeof(AbpRulesEngineModule),
typeof(AbpTestsBaseModule))]
public class AbpRulesEngineTestModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpRulesEnginePhysicalFileResolveOptions>(options =>
{
options.PhysicalPath = Path.Combine(Directory.GetCurrentDirectory(), "Rules");
});
}
}
}

235
aspnet-core/tests/LINGYUN.Abp.Rules.RulesEngine.Tests/LINGYUN/Abp/Rules/RulesEngine/TestInputRuleTests.cs

@ -1,117 +1,118 @@
using Shouldly;
using System.Threading.Tasks;
using Volo.Abp.Validation;
using Xunit;
namespace LINGYUN.Abp.Rules.RulesEngine
{
public class TestInputRuleTests : AbpRulesEngineTestBase
{
private readonly IRuleProvider _ruleProvider;
public TestInputRuleTests()
{
_ruleProvider = GetRequiredService<IRuleProvider>();
}
[Fact]
public async Task Input_Required_Should_Required()
{
var input = new TestInput
{
Integer1 = 101,
Integer2 = 99,
Length = "123456"
}
;
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _ruleProvider.ExecuteAsync(input);
});
exception.Message.ShouldBe("一个或多个规则未通过");
exception.ValidationErrors.Count.ShouldBe(1);
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Required 必须输入!");
}
[Fact]
public async Task Input_Integer1_Should_MustBeGreaterThan100()
{
var input = new TestInput
{
Required = "123456",
Integer1 = 99,
Integer2 = 99,
Length = "123456"
};
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _ruleProvider.ExecuteAsync(input);
});
exception.Message.ShouldBe("一个或多个规则未通过");
exception.ValidationErrors.Count.ShouldBe(1);
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Integer1 必须大于100!");
}
[Fact]
public async Task Input_Integer2_Should_MustBeLessThan100()
{
var input = new TestInput
{
Required = "123456",
Integer1 = 101,
Integer2 = 100,
Length = "123456"
};
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _ruleProvider.ExecuteAsync(input);
});
exception.Message.ShouldBe("一个或多个规则未通过");
exception.ValidationErrors.Count.ShouldBe(1);
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Integer2 必须小于100!");
}
[Fact]
public async Task Input_Sum_Integer1_And_Integer2_Should_MustBeGreaterThan150()
{
var input = new TestInput
{
Required = "1",
Integer1 = 101,
Integer2 = 48,
Length = "123456"
};
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _ruleProvider.ExecuteAsync(input);
});
exception.Message.ShouldBe("一个或多个规则未通过");
exception.ValidationErrors.Count.ShouldBe(1);
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Integer1 与 Integer2 之和 必须大于150!");
}
[Fact]
public async Task Input_Required_Length_Should_MustBeGreaterThan5()
{
var input = new TestInput
{
Required = "1",
Integer1 = 101,
Integer2 = 50,
Length = "12345"
};
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _ruleProvider.ExecuteAsync(input);
});
exception.Message.ShouldBe("一个或多个规则未通过");
exception.ValidationErrors.Count.ShouldBe(1);
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Length 长度必须大于5!");
}
}
}
using Shouldly;
using System;
using System.Threading.Tasks;
using Volo.Abp.Validation;
using Xunit;
namespace LINGYUN.Abp.Rules.RulesEngine
{
public class TestInputRuleTests : AbpRulesEngineTestBase
{
private readonly IRuleProvider _ruleProvider;
public TestInputRuleTests()
{
_ruleProvider = GetRequiredService<IRuleProvider>();
}
[Fact]
public async Task Input_Required_Should_Required()
{
var input = new TestInput
{
Integer1 = 101,
Integer2 = 99,
Length = "123456"
}
;
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _ruleProvider.ExecuteAsync(input);
});
exception.Message.ShouldBe("一个或多个规则未通过");
exception.ValidationErrors.Count.ShouldBe(1);
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Required 必须输入!");
}
[Fact]
public async Task Input_Integer1_Should_MustBeGreaterThan100()
{
var input = new TestInput
{
Required = "123456",
Integer1 = 99,
Integer2 = 99,
Length = "123456"
};
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _ruleProvider.ExecuteAsync(input);
});
exception.Message.ShouldBe("一个或多个规则未通过");
exception.ValidationErrors.Count.ShouldBe(1);
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Integer1 必须大于100!");
}
[Fact]
public async Task Input_Integer2_Should_MustBeLessThan100()
{
var input = new TestInput
{
Required = "123456",
Integer1 = 101,
Integer2 = 100,
Length = "123456"
};
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _ruleProvider.ExecuteAsync(input);
});
exception.Message.ShouldBe("一个或多个规则未通过");
exception.ValidationErrors.Count.ShouldBe(1);
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Integer2 必须小于100!");
}
[Fact]
public async Task Input_Sum_Integer1_And_Integer2_Should_MustBeGreaterThan150()
{
var input = new TestInput
{
Required = "1",
Integer1 = 101,
Integer2 = 48,
Length = "123456"
};
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _ruleProvider.ExecuteAsync(input);
});
exception.Message.ShouldBe("一个或多个规则未通过");
exception.ValidationErrors.Count.ShouldBe(1);
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Integer1 与 Integer2 之和 必须大于150!");
}
[Fact]
public async Task Input_Required_Length_Should_MustBeGreaterThan5()
{
var input = new TestInput
{
Required = "1",
Integer1 = 101,
Integer2 = 50,
Length = "12345"
};
var exception = await Assert.ThrowsAsync<AbpValidationException>(async () =>
{
await _ruleProvider.ExecuteAsync(input);
});
exception.Message.ShouldBe("一个或多个规则未通过");
exception.ValidationErrors.Count.ShouldBe(1);
exception.ValidationErrors[0].ErrorMessage.ShouldBe("字段 Length 长度必须大于5!");
}
}
}

Loading…
Cancel
Save