Skip to content

Commit ac4ca5f

Browse files
authored
Merge pull request #25621 from abpframework/auto-merge/rel-10-4/4649
Merge branch rel-10.5 with rel-10.4
2 parents cbc1a75 + 4d3d8b5 commit ac4ca5f

7 files changed

Lines changed: 422 additions & 0 deletions

File tree

framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/AbpDddApplicationModule.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ namespace Volo.Abp.Application;
3030
)]
3131
public class AbpDddApplicationModule : AbpModule
3232
{
33+
public override void PreConfigureServices(ServiceConfigurationContext context)
34+
{
35+
AbpDynamicSortingGuard.Install();
36+
}
37+
3338
public override void ConfigureServices(ServiceConfigurationContext context)
3439
{
3540
Configure<AbpApiDescriptionModelOptions>(options =>
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
using System;
2+
using System.Linq;
3+
using System.Linq.Dynamic.Core;
4+
using System.Linq.Expressions;
5+
using System.Runtime.CompilerServices;
6+
using Volo.Abp.Validation;
7+
8+
[assembly: InternalsVisibleTo("Volo.Abp.Ddd.Application.Tests")]
9+
10+
namespace Volo.Abp.Application.Services;
11+
12+
/// <summary>
13+
/// Framework infrastructure. Hooks <see cref="ExtensibilityPoint.QueryOptimizer"/> so
14+
/// every OrderBy / ThenBy expression built from a user-supplied sorting string is
15+
/// constrained to plain property or field access. Methods, comparisons, ternaries
16+
/// and constants in the sort key are rejected with <see cref="AbpValidationException"/>.
17+
/// </summary>
18+
internal static class AbpDynamicSortingGuard
19+
{
20+
private static readonly object InstallLock = new();
21+
private static Func<Expression, Expression>? _activeOptimizer;
22+
23+
public static void Install()
24+
{
25+
lock (InstallLock)
26+
{
27+
var current = ExtensibilityPoint.QueryOptimizer;
28+
if (_activeOptimizer != null && ReferenceEquals(current, _activeOptimizer))
29+
{
30+
return;
31+
}
32+
33+
var previous = current;
34+
_activeOptimizer = expression =>
35+
{
36+
new OrderByMethodVisitor().Visit(expression);
37+
return previous != null ? previous(expression) : expression;
38+
};
39+
ExtensibilityPoint.QueryOptimizer = _activeOptimizer;
40+
}
41+
}
42+
43+
internal static void Reset()
44+
{
45+
lock (InstallLock)
46+
{
47+
if (ReferenceEquals(ExtensibilityPoint.QueryOptimizer, _activeOptimizer))
48+
{
49+
ExtensibilityPoint.QueryOptimizer = null;
50+
}
51+
_activeOptimizer = null;
52+
}
53+
}
54+
55+
private sealed class OrderByMethodVisitor : ExpressionVisitor
56+
{
57+
protected override Expression VisitMethodCall(MethodCallExpression node)
58+
{
59+
if (node.Method.DeclaringType == typeof(Queryable) &&
60+
IsOrderByMethod(node.Method.Name) &&
61+
node.Arguments.Count >= 2 &&
62+
node.Arguments[1] is UnaryExpression { Operand: LambdaExpression lambda })
63+
{
64+
new PropertyOnlySelectorVisitor().Visit(lambda.Body);
65+
}
66+
67+
return base.VisitMethodCall(node);
68+
}
69+
70+
private static bool IsOrderByMethod(string name)
71+
{
72+
return name == nameof(Queryable.OrderBy)
73+
|| name == nameof(Queryable.OrderByDescending)
74+
|| name == nameof(Queryable.ThenBy)
75+
|| name == nameof(Queryable.ThenByDescending);
76+
}
77+
}
78+
79+
private sealed class PropertyOnlySelectorVisitor : ExpressionVisitor
80+
{
81+
private const string Message = "Sorting expression is not supported.";
82+
83+
protected override Expression VisitMethodCall(MethodCallExpression node)
84+
=> throw new AbpValidationException(Message);
85+
86+
protected override Expression VisitBinary(BinaryExpression node)
87+
=> throw new AbpValidationException(Message);
88+
89+
protected override Expression VisitConditional(ConditionalExpression node)
90+
=> throw new AbpValidationException(Message);
91+
92+
protected override Expression VisitConstant(ConstantExpression node)
93+
=> throw new AbpValidationException(Message);
94+
}
95+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"role": "lib.test"
3+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<Import Project="..\..\..\common.test.props" />
4+
5+
<PropertyGroup>
6+
<TargetFramework>net10.0</TargetFramework>
7+
<RootNamespace />
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
12+
<ProjectReference Include="..\..\src\Volo.Abp.Ddd.Application\Volo.Abp.Ddd.Application.csproj" />
13+
<ProjectReference Include="..\..\src\Volo.Abp.ExceptionHandling\Volo.Abp.ExceptionHandling.csproj" />
14+
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
15+
<PackageReference Include="Microsoft.NET.Test.Sdk" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Volo.Abp.Modularity;
2+
using Volo.Abp.Testing;
3+
4+
namespace Volo.Abp.Application;
5+
6+
public abstract class AbpDddApplicationTestBase : AbpIntegratedTest<AbpDddApplicationTestModule>
7+
{
8+
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
9+
{
10+
options.UseAutofac();
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using Volo.Abp.Autofac;
2+
using Volo.Abp.ExceptionHandling;
3+
using Volo.Abp.Modularity;
4+
5+
namespace Volo.Abp.Application;
6+
7+
[DependsOn(typeof(AbpAutofacModule))]
8+
[DependsOn(typeof(AbpDddApplicationModule))]
9+
[DependsOn(typeof(AbpExceptionHandlingModule))]
10+
public class AbpDddApplicationTestModule : AbpModule
11+
{
12+
}

0 commit comments

Comments
 (0)