Skip to content
This repository was archived by the owner on Feb 17, 2025. It is now read-only.

Commit fc320d5

Browse files
committed
Merge remote-tracking branch 'origin/develop' into workflow
2 parents 5ec461a + 6d65555 commit fc320d5

File tree

5 files changed

+110
-6
lines changed

5 files changed

+110
-6
lines changed
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Issue 33 : Recursive loop of complex types
2+
3+
namespace Community.OData.Linq.xTests.Issues33
4+
{
5+
using System;
6+
using System.Linq;
7+
using Xunit;
8+
9+
public class RecursiveComplexType
10+
{
11+
public RecursiveComplexType SelfReference { get; set; }
12+
}
13+
14+
public class ListItem
15+
{
16+
public int Id { get; set; }
17+
18+
public RecursiveComplexType RecursiveComplexType { get; set; }
19+
}
20+
21+
public class Issue33
22+
{
23+
[Fact]
24+
public void Recursive_Loops_Must_Not_Be_Allowed_By_Default()
25+
{
26+
// arrange
27+
28+
var queryable = new[]
29+
{
30+
new ListItem { Id = 1, RecursiveComplexType = new() },
31+
new ListItem { Id = 2, RecursiveComplexType = new() }
32+
}.AsQueryable();
33+
34+
// act
35+
36+
var exception = Assert.Throws<ArgumentException>(() => queryable.OData().Filter("Id eq 1").ToArray());
37+
38+
// assert
39+
40+
Assert.Contains("recursive loop of complex types is not allowed", exception.Message);
41+
}
42+
43+
[Fact]
44+
public void Recursive_Loops_Must_Be_Allowed_If_Opted_Into_By_Inline_Configuration()
45+
{
46+
// arrange
47+
48+
var queryable = new[]
49+
{
50+
new ListItem { Id = 1, RecursiveComplexType = new() },
51+
new ListItem { Id = 2, RecursiveComplexType = new() }
52+
53+
}.AsQueryable();
54+
55+
// act
56+
57+
var result = queryable.OData(x =>
58+
{
59+
x.AllowRecursiveLoopOfComplexTypes = true;
60+
61+
}).Filter("Id eq 1").ToArray();
62+
63+
// assert
64+
65+
Assert.Single(result);
66+
}
67+
68+
[Fact]
69+
public void Recursive_Loops_Must_Be_Allowed_If_Opted_Into_By_Global_Configuration()
70+
{
71+
// arrange
72+
73+
var queryable = new[]
74+
{
75+
new ListItem { Id = 1, RecursiveComplexType = new() },
76+
new ListItem { Id = 2, RecursiveComplexType = new() }
77+
78+
}.AsQueryable();
79+
80+
ODataSettings.SetInitializer(x =>
81+
{
82+
x.AllowRecursiveLoopOfComplexTypes = true;
83+
84+
});
85+
86+
// act
87+
88+
var result = queryable.OData().Filter("Id eq 1").ToArray();
89+
90+
// assert
91+
92+
Assert.Single(result);
93+
}
94+
}
95+
}

Community.Data.OData.Linq/Builder/ODataModelBuilder.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ public virtual IEnumerable<OperationConfiguration> Operations
140140
get { return this._operations; }
141141
}
142142

143+
/// <summary>
144+
/// Gets/Sets the configured settings
145+
/// </summary>
146+
public ODataSettings ODataSettings { get; set; }
147+
143148
/// <summary>
144149
/// Gets or sets the navigation property binding options.
145150
/// </summary>

Community.Data.OData.Linq/Builder/StructuralTypeConfiguration.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ public virtual ComplexPropertyConfiguration AddComplexProperty(PropertyInfo prop
383383
throw Error.Argument("propertyInfo", SRResources.PropertyDoesNotBelongToType, propertyInfo.Name, this.ClrType.FullName);
384384
}
385385

386-
if (propertyInfo.PropertyType == this.ClrType)
386+
if (propertyInfo.PropertyType == this.ClrType && ModelBuilder?.ODataSettings?.AllowRecursiveLoopOfComplexTypes != true)
387387
{
388388
throw Error.Argument("propertyInfo", SRResources.RecursiveComplexTypesNotAllowed, this.ClrType.FullName, propertyInfo.Name);
389389
}

Community.Data.OData.Linq/ODataSettings.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public static void SetInitializer(Action<ODataSettings> initializer)
4949

5050
public bool EnableCaseInsensitive { get; set; } = true;
5151

52+
public bool AllowRecursiveLoopOfComplexTypes { get; set; } = false;
53+
5254
public DefaultQuerySettings DefaultQuerySettings { get; } =
5355
new DefaultQuerySettings
5456
{

Community.Data.OData.Linq/OdataLinqExtensions.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,17 @@ public static ODataQuery<T> OData<T>(this IQueryable<T> query, Action<ODataSetti
5454
{
5555
if (query == null) throw new ArgumentNullException(nameof(query));
5656

57+
ODataSettings settings = new ODataSettings();
58+
configuration?.Invoke(settings);
59+
5760
if (edmModel == null)
5861
{
5962
edmModel = Models.GetOrAdd(typeof(T), t =>
6063
{
6164
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
65+
builder.ODataSettings = settings;
6266
builder.AddEntityType(t);
63-
builder.AddEntitySet(t.Name, new EntityTypeConfiguration(new ODataModelBuilder(), t));
67+
builder.AddEntitySet(t.Name, new EntityTypeConfiguration(builder, t));
6468
return builder.GetEdmModel();
6569
});
6670
}
@@ -72,14 +76,12 @@ public static ODataQuery<T> OData<T>(this IQueryable<T> query, Action<ODataSetti
7276
}
7377
}
7478

75-
ODataSettings settings = new ODataSettings();
76-
configuration?.Invoke(settings);
77-
7879
int settingsHash = HashCode.Combine(
7980
settings.QuerySettings,
8081
settings.DefaultQuerySettings,
8182
settings.ParserSettings.MaximumExpansionCount,
82-
settings.ParserSettings.MaximumExpansionDepth);
83+
settings.ParserSettings.MaximumExpansionDepth,
84+
settings.AllowRecursiveLoopOfComplexTypes);
8385

8486
ServiceContainer baseContainer = Containers.GetOrAdd(settingsHash, i =>
8587
{

0 commit comments

Comments
 (0)