-
-
Notifications
You must be signed in to change notification settings - Fork 152
Commit
MaxDepth
for recursive queries
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -97,4 +97,9 @@ public class MapperAttribute : Attribute | |
/// Determines the access level of members that Mapperly will map. | ||
/// </summary> | ||
public MemberVisibility IncludedMembers { get; set; } = MemberVisibility.AllAccessible; | ||
|
||
/// <summary> | ||
/// Defines the maximum recursive depth that an IQueryable mapping will use. | ||
/// </summary> | ||
public int MaxRecursiveDepth { get; set; } = 8; | ||
Check failure on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / test
Check failure on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / test
Check failure on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / test
Check failure on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / test
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / sample
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / sample
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / package
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / package
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / lint-dotnet
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / lint-dotnet
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / SourceGeneratorBenchmarks
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / SourceGeneratorBenchmarks
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / build
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / build
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / build
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / MappingBenchmarks
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / MappingBenchmarks
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / Analyze (csharp)
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / Analyze (csharp)
Check warning on line 104 in src/Riok.Mapperly.Abstractions/MapperAttribute.cs GitHub Actions / Analyze (csharp)
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
namespace Riok.Mapperly.Abstractions; | ||
|
||
/// <summary> | ||
/// Defines the maximum recursive depth that an IQueryable mapping will use. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Method)] | ||
public sealed class MapperMaxRecursiveDepthAttribute : Attribute | ||
Check failure on line 7 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / test
Check failure on line 7 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / test
Check warning on line 7 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / sample
Check warning on line 7 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / package
Check warning on line 7 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / lint-dotnet
Check warning on line 7 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / SourceGeneratorBenchmarks
Check warning on line 7 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / build
Check warning on line 7 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / build
Check warning on line 7 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / MappingBenchmarks
Check warning on line 7 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / Analyze (csharp)
Check warning on line 7 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / Analyze (csharp)
|
||
{ | ||
/// <summary> | ||
/// Defines the maximum recursive depth that an IQueryable mapping will use. | ||
/// </summary> | ||
/// <param name="maxRecursiveDepth">The maximum recursive depth used when mapping IQueryable members.</param> | ||
public MapperMaxRecursiveDepthAttribute(int maxRecursiveDepth) | ||
Check failure on line 13 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / test
Check failure on line 13 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / test
Check warning on line 13 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / sample
Check warning on line 13 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / package
Check warning on line 13 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / lint-dotnet
Check warning on line 13 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / SourceGeneratorBenchmarks
Check warning on line 13 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / build
Check warning on line 13 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / MappingBenchmarks
Check warning on line 13 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / Analyze (csharp)
|
||
{ | ||
MaxRecursiveDepth = maxRecursiveDepth; | ||
} | ||
|
||
/// <summary> | ||
/// The maximum recursive depth used when mapping IQueryable members. | ||
/// </summary> | ||
public int MaxRecursiveDepth { get; } | ||
Check failure on line 21 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / test
Check failure on line 21 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / test
Check warning on line 21 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / sample
Check warning on line 21 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / package
Check warning on line 21 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / lint-dotnet
Check warning on line 21 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / SourceGeneratorBenchmarks
Check warning on line 21 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / build
Check warning on line 21 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / MappingBenchmarks
Check warning on line 21 in src/Riok.Mapperly.Abstractions/MapperMaxRecursiveDepthAttribute.cs GitHub Actions / Analyze (csharp)
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using static Riok.Mapperly.Emit.Syntax.SyntaxFactoryHelper; | ||
|
||
namespace Riok.Mapperly.Descriptors.Mappings; | ||
|
||
/// <summary> | ||
/// Represents a mapping that returns default. | ||
/// <code> | ||
/// target = default; | ||
/// </code> | ||
/// </summary> | ||
public class DefaultMemberMapping(ITypeSymbol sourceType, ITypeSymbol targetType) : NewInstanceMapping(sourceType, targetType) | ||
{ | ||
public override ExpressionSyntax Build(TypeMappingBuildContext ctx) => DefaultLiteral(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -482,4 +482,13 @@ public static class DiagnosticDescriptors | |
DiagnosticSeverity.Error, | ||
true | ||
); | ||
|
||
public static readonly DiagnosticDescriptor MaxRecursiveDepthMustBeZeroOrMore = new DiagnosticDescriptor( | ||
"RMG056", | ||
Check warning on line 487 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / sample
Check warning on line 487 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / package
Check warning on line 487 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / lint-dotnet
Check warning on line 487 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / SourceGeneratorBenchmarks
Check warning on line 487 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / build
Check warning on line 487 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / MappingBenchmarks
Check warning on line 487 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / Analyze (csharp)
|
||
$"The value {nameof(MapperAttribute.MaxRecursiveDepth)} cannot be less than zero.", | ||
Check warning on line 488 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / sample
Check warning on line 488 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / package
Check warning on line 488 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / package
Check warning on line 488 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / lint-dotnet
Check warning on line 488 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / SourceGeneratorBenchmarks
Check warning on line 488 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / build
Check warning on line 488 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / MappingBenchmarks
|
||
$"The value {nameof(MapperAttribute.MaxRecursiveDepth)} cannot be less than zero.", | ||
Check warning on line 489 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / sample
Check warning on line 489 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / package
Check warning on line 489 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / package
Check warning on line 489 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / lint-dotnet
Check warning on line 489 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / SourceGeneratorBenchmarks
Check warning on line 489 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / build
Check warning on line 489 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / MappingBenchmarks
Check warning on line 489 in src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs GitHub Actions / Analyze (csharp)
|
||
DiagnosticCategories.Mapper, | ||
DiagnosticSeverity.Error, | ||
true | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
using Riok.Mapperly.Diagnostics; | ||
|
||
namespace Riok.Mapperly.Tests.Mapping; | ||
|
||
[UsesVerify] | ||
public class QueryableProjectionLoopTest | ||
{ | ||
[Fact] | ||
public Task ReferenceLoopInitProperty() | ||
{ | ||
var source = TestSourceBuilder.Mapping( | ||
"System.Linq.IQueryable<A>", | ||
"System.Linq.IQueryable<B>", | ||
"class A { public A? Parent { get; set; } }", | ||
"class B { public B? Parent { get; set; } }" | ||
); | ||
|
||
return TestHelper.VerifyGenerator(source); | ||
} | ||
|
||
[Fact] | ||
public Task SetRecursiveDepthForLoop() | ||
{ | ||
var source = TestSourceBuilder.Mapping( | ||
"System.Linq.IQueryable<A>", | ||
"System.Linq.IQueryable<B>", | ||
TestSourceBuilderOptions.Default with | ||
{ | ||
MaxRecursiveDepth = 2 | ||
}, | ||
"class A { public A? Parent { get; set; } }", | ||
"class B { public B? Parent { get; set; } }" | ||
); | ||
|
||
return TestHelper.VerifyGenerator(source); | ||
} | ||
|
||
[Fact] | ||
public Task MethodAttributeSetRecursiveDepthForLoop() | ||
{ | ||
var source = TestSourceBuilder.MapperWithBodyAndTypes( | ||
"[MapperMaxRecursiveDepth(2)] " | ||
+ "partial System.Linq.IQueryable<B> Map(System.Linq.IQueryable<A> src);" | ||
+ "[MapperMaxRecursiveDepth(2)] partial B Map(A src);", | ||
"class A { public A? Parent { get; set; } }", | ||
"class B { public B? Parent { get; set; } }" | ||
); | ||
|
||
return TestHelper.VerifyGenerator(source); | ||
} | ||
|
||
[Fact] | ||
public Task MethodAttributeOverridesClassSetRecursiveDepthForLoop() | ||
{ | ||
var source = TestSourceBuilder.MapperWithBodyAndTypes( | ||
"[MapperMaxRecursiveDepth(2)] " | ||
+ "partial System.Linq.IQueryable<B> Map(System.Linq.IQueryable<A> src);" | ||
+ "[MapperMaxRecursiveDepth(2)] partial B Map(A src);", | ||
TestSourceBuilderOptions.Default with | ||
{ | ||
MaxRecursiveDepth = 4 | ||
}, | ||
"class A { public A? Parent { get; set; } }", | ||
"class B { public B? Parent { get; set; } }" | ||
); | ||
|
||
return TestHelper.VerifyGenerator(source); | ||
} | ||
|
||
[Fact] | ||
public Task AssemblyDefaultShouldWork() | ||
{ | ||
var source = TestSourceBuilder.CSharp( | ||
""" | ||
using Riok.Mapperly.Abstractions; | ||
[assembly: MapperDefaultsAttribute(MaxRecursiveDepth = 2)] | ||
[Mapper()] | ||
public partial class MyMapper | ||
{ | ||
private partial B Map(A source); | ||
} | ||
class A { public A? Parent { get; set; } } | ||
class B { public B? Parent { get; set; } } | ||
""" | ||
); | ||
|
||
return TestHelper.VerifyGenerator(source); | ||
} | ||
|
||
[Fact] | ||
public Task AttributeShouldOverrideAssemblyDefault() | ||
{ | ||
var source = TestSourceBuilder.CSharp( | ||
""" | ||
using Riok.Mapperly.Abstractions; | ||
[assembly: MapperDefaultsAttribute(MaxRecursiveDepth = 2)] | ||
[Mapper(MaxRecursiveDepth = 4)] | ||
public partial class MyMapper | ||
{ | ||
private partial B Map(A source); | ||
} | ||
class A { public A? Parent { get; set; } } | ||
class B { public B? Parent { get; set; } } | ||
""" | ||
); | ||
|
||
return TestHelper.VerifyGenerator(source); | ||
} | ||
|
||
[Fact] | ||
public Task ReferenceLoopCtor() | ||
{ | ||
var source = TestSourceBuilder.Mapping( | ||
"System.Linq.IQueryable<A>", | ||
"System.Linq.IQueryable<B>", | ||
"class A { public A? Parent { get; set; } }", | ||
"class B { public B(B? parent) {} }" | ||
); | ||
|
||
return TestHelper.VerifyGenerator(source); | ||
} | ||
|
||
[Fact] | ||
public Task IndirectReferenceLoop() | ||
{ | ||
var source = TestSourceBuilder.Mapping( | ||
"System.Linq.IQueryable<A>", | ||
"System.Linq.IQueryable<B>", | ||
"class A { public string StringValue { get; set; } public C Parent { get; set; } }", | ||
"class B { public string StringValue { get; set; } public D Parent { get; set; } }", | ||
"class C { public A Parent { get; set; } }", | ||
"class D { public B Parent { get; set; } }" | ||
); | ||
|
||
return TestHelper.VerifyGenerator(source); | ||
} | ||
|
||
[Fact] | ||
public void WithReferenceHandlingShouldDiagnostic() | ||
{ | ||
var source = TestSourceBuilder.Mapping( | ||
"System.Linq.IQueryable<long>", | ||
"System.Linq.IQueryable<int>", | ||
TestSourceBuilderOptions.WithReferenceHandling | ||
); | ||
|
||
TestHelper | ||
.GenerateMapper(source, TestHelperOptions.AllowDiagnostics) | ||
.Should() | ||
.HaveDiagnostic(DiagnosticDescriptors.QueryableProjectionMappingsDoNotSupportReferenceHandling) | ||
.HaveAssertedAllDiagnostics(); | ||
} | ||
} |