Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ namespace Volo.Abp.Application;
)]
public class AbpDddApplicationModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
AbpDynamicSortingGuard.Install();
}

public override void ConfigureServices(ServiceConfigurationContext context)
{
Configure<AbpApiDescriptionModelOptions>(options =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
using System;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using Volo.Abp.Validation;

[assembly: InternalsVisibleTo("Volo.Abp.Ddd.Application.Tests")]

namespace Volo.Abp.Application.Services;

/// <summary>
/// Framework infrastructure. Hooks <see cref="ExtensibilityPoint.QueryOptimizer"/> so
/// every OrderBy / ThenBy expression built from a user-supplied sorting string is
/// constrained to plain property or field access. Methods, comparisons, ternaries
/// and constants in the sort key are rejected with <see cref="AbpValidationException"/>.
/// </summary>
internal static class AbpDynamicSortingGuard
{
private static readonly object InstallLock = new();
private static Func<Expression, Expression>? _activeOptimizer;

public static void Install()
{
lock (InstallLock)
{
var current = ExtensibilityPoint.QueryOptimizer;
if (_activeOptimizer != null && ReferenceEquals(current, _activeOptimizer))
{
return;
}

var previous = current;
_activeOptimizer = expression =>
{
new OrderByMethodVisitor().Visit(expression);
return previous != null ? previous(expression) : expression;
};
ExtensibilityPoint.QueryOptimizer = _activeOptimizer;
}
}

internal static void Reset()
{
lock (InstallLock)
{
if (ReferenceEquals(ExtensibilityPoint.QueryOptimizer, _activeOptimizer))
{
ExtensibilityPoint.QueryOptimizer = null;
}
_activeOptimizer = null;
}
}

private sealed class OrderByMethodVisitor : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.DeclaringType == typeof(Queryable) &&
IsOrderByMethod(node.Method.Name) &&
node.Arguments.Count >= 2 &&
node.Arguments[1] is UnaryExpression { Operand: LambdaExpression lambda })
{
new PropertyOnlySelectorVisitor().Visit(lambda.Body);
}

return base.VisitMethodCall(node);
}

private static bool IsOrderByMethod(string name)
{
return name == nameof(Queryable.OrderBy)
|| name == nameof(Queryable.OrderByDescending)
|| name == nameof(Queryable.ThenBy)
|| name == nameof(Queryable.ThenByDescending);
}
}

private sealed class PropertyOnlySelectorVisitor : ExpressionVisitor
{
private const string Message = "Sorting expression is not supported.";

protected override Expression VisitMethodCall(MethodCallExpression node)
=> throw new AbpValidationException(Message);

protected override Expression VisitBinary(BinaryExpression node)
=> throw new AbpValidationException(Message);

protected override Expression VisitConditional(ConditionalExpression node)
=> throw new AbpValidationException(Message);

protected override Expression VisitConstant(ConstantExpression node)
=> throw new AbpValidationException(Message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"role": "lib.test"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<Import Project="..\..\..\common.test.props" />

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<RootNamespace />
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.Ddd.Application\Volo.Abp.Ddd.Application.csproj" />
<ProjectReference Include="..\..\src\Volo.Abp.ExceptionHandling\Volo.Abp.ExceptionHandling.csproj" />
<ProjectReference Include="..\AbpTestBase\AbpTestBase.csproj" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Volo.Abp.Modularity;
using Volo.Abp.Testing;

namespace Volo.Abp.Application;

public abstract class AbpDddApplicationTestBase : AbpIntegratedTest<AbpDddApplicationTestModule>
{
protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
{
options.UseAutofac();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Volo.Abp.Autofac;
using Volo.Abp.ExceptionHandling;
using Volo.Abp.Modularity;

namespace Volo.Abp.Application;

[DependsOn(typeof(AbpAutofacModule))]
[DependsOn(typeof(AbpDddApplicationModule))]
[DependsOn(typeof(AbpExceptionHandlingModule))]
public class AbpDddApplicationTestModule : AbpModule
{
}
Loading
Loading