Skip to content

Commit f7b42ed

Browse files
authored
Add config setting for PrioritizePropertyOrFieldOverTheType (#664)
* ... * Add config setting for PrioritizePropertyOrFieldOverTheType
1 parent f4bfa99 commit f7b42ed

10 files changed

+542
-438
lines changed

src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs

+15-7
Original file line numberDiff line numberDiff line change
@@ -857,9 +857,12 @@ private Expression ParseIdentifier()
857857
{
858858
_textParser.ValidateToken(TokenId.Identifier);
859859

860-
if (_keywordsHelper.TryGetValue(_textParser.CurrentToken.Text, out object? value) &&
861-
// Prioritize property or field over the type
862-
!(value is Type && _it != null && FindPropertyOrField(_it.Type, _textParser.CurrentToken.Text, false) != null))
860+
var isValidKeyWord = _keywordsHelper.TryGetValue(_textParser.CurrentToken.Text, out var value);
861+
862+
var extraCondition = !_parsingConfig.PrioritizePropertyOrFieldOverTheType ||
863+
(_parsingConfig.PrioritizePropertyOrFieldOverTheType && !(value is Type && _it != null && FindPropertyOrField(_it.Type, _textParser.CurrentToken.Text, false) != null));
864+
865+
if (isValidKeyWord && extraCondition)
863866
{
864867
if (value is Type typeValue)
865868
{
@@ -1711,9 +1714,13 @@ private Expression ParseMemberAccess(Type? type, Expression? expression)
17111714
return Expression.Field(expression, field);
17121715
}
17131716

1714-
if (!_parsingConfig.DisableMemberAccessToIndexAccessorFallback && expression != null)
1717+
// #357 #662
1718+
var extraCheck = !_parsingConfig.PrioritizePropertyOrFieldOverTheType ||
1719+
_parsingConfig.PrioritizePropertyOrFieldOverTheType && expression != null;
1720+
1721+
if (!_parsingConfig.DisableMemberAccessToIndexAccessorFallback && extraCheck)
17151722
{
1716-
var indexerMethod = expression.Type.GetMethod("get_Item", new[] { typeof(string) });
1723+
var indexerMethod = expression?.Type.GetMethod("get_Item", new[] { typeof(string) });
17171724
if (indexerMethod != null)
17181725
{
17191726
return Expression.Call(expression, indexerMethod, Expression.Constant(id));
@@ -2139,11 +2146,12 @@ private static Exception IncompatibleOperandsError(string opName, Expression lef
21392146
private MemberInfo? FindPropertyOrField(Type type, string memberName, bool staticAccess)
21402147
{
21412148
#if !(NETFX_CORE || WINDOWS_APP || UAP10_0 || NETSTANDARD)
2142-
BindingFlags flags = BindingFlags.Public | BindingFlags.DeclaredOnly | (staticAccess ? BindingFlags.Static : BindingFlags.Instance);
2149+
var extraBindingFlag = _parsingConfig.PrioritizePropertyOrFieldOverTheType && staticAccess ? BindingFlags.Static : BindingFlags.Instance;
2150+
var bindingFlags = BindingFlags.Public | BindingFlags.DeclaredOnly | extraBindingFlag;
21432151
foreach (Type t in TypeHelper.GetSelfAndBaseTypes(type))
21442152
{
21452153
var findMembersType = _parsingConfig?.IsCaseSensitive == true ? Type.FilterName : Type.FilterNameIgnoreCase;
2146-
var members = t.FindMembers(MemberTypes.Property | MemberTypes.Field, flags, findMembersType, memberName);
2154+
var members = t.FindMembers(MemberTypes.Property | MemberTypes.Field, bindingFlags, findMembersType, memberName);
21472155

21482156
if (members.Length != 0)
21492157
{

src/System.Linq.Dynamic.Core/ParsingConfig.cs

+9
Original file line numberDiff line numberDiff line change
@@ -211,5 +211,14 @@ public IQueryableAnalyzer QueryableAnalyzer
211211
/// Default value is <c>true</c>.
212212
/// </summary>
213213
public bool SupportCastingToFullyQualifiedTypeAsString { get; set; } = true;
214+
215+
/// <summary>
216+
/// When the type and property have the same name the parser takes the property instead of type when this setting is set to <c>true</c>.
217+
///
218+
/// The value from this setting should also be set to <c>true</c> when ExtensionMethods are used.
219+
///
220+
/// Default value is <c>false</c>.
221+
/// </summary>
222+
public bool PrioritizePropertyOrFieldOverTheType { get; set; }
214223
}
215224
}

test/System.Linq.Dynamic.Core.Tests/DynamicExpressionParserTests.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1449,9 +1449,10 @@ public void DynamicExpressionParser_ParseLambda_GenericExtensionMethod()
14491449
{
14501450
// Arrange
14511451
var testList = User.GenerateSampleModels(51);
1452-
var config = new ParsingConfig()
1452+
var config = new ParsingConfig
14531453
{
1454-
CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod()
1454+
CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForGenericExtensionMethod(),
1455+
PrioritizePropertyOrFieldOverTheType = true
14551456
};
14561457

14571458
// Act
@@ -1461,7 +1462,6 @@ public void DynamicExpressionParser_ParseLambda_GenericExtensionMethod()
14611462

14621463
var result = Enumerable.Where(testList, del);
14631464

1464-
14651465
var expected = testList.Where(x => new string[] { "User4", "User2" }.Contains(x.UserName)).ToList();
14661466

14671467
// Assert
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
using System.Collections.Generic;
22

3-
namespace System.Linq.Dynamic.Core.Tests.Entities
3+
namespace System.Linq.Dynamic.Core.Tests.Entities;
4+
5+
public class Company : Entity
46
{
5-
public class Company : Entity
6-
{
7-
public string Name { get; set; }
7+
public string Name { get; set; }
8+
9+
public long? MainCompanyId { get; set; }
810

9-
public long? MainCompanyId { get; set; }
11+
public MainCompany MainCompany { get; set; }
1012

11-
public MainCompany MainCompany { get; set; }
13+
public ICollection<Employee> Employees { get; set; }
1214

13-
public ICollection<Employee> Employees { get; set; }
14-
}
15+
public DateTime DateTime { get; set; }
1516
}

test/System.Linq.Dynamic.Core.Tests/ExpressionTests.cs

+12-3
Original file line numberDiff line numberDiff line change
@@ -290,14 +290,18 @@ public void ExpressionTests_Cast_To_Enum_Using_DynamicLinqType()
290290
public void ExpressionTests_Cast_To_FullTypeDateTime_Using_DynamicLinqType()
291291
{
292292
// Arrange
293+
var config = new ParsingConfig
294+
{
295+
PrioritizePropertyOrFieldOverTheType = true
296+
};
293297
var list = new List<SimpleValuesModel>
294298
{
295299
new SimpleValuesModel { DateTime = DateTime.Now }
296300
};
297301

298302
// Act
299303
var expectedResult = list.Select(x => x.DateTime);
300-
var result = list.AsQueryable().Select($"\"{typeof(DateTime).FullName}\"(DateTime)");
304+
var result = list.AsQueryable().Select(config, $"\"{typeof(DateTime).FullName}\"(DateTime)");
301305

302306
// Assert
303307
Assert.Equal(expectedResult.ToArray(), result.ToDynamicArray<DateTime>());
@@ -307,14 +311,18 @@ public void ExpressionTests_Cast_To_FullTypeDateTime_Using_DynamicLinqType()
307311
public void ExpressionTests_Cast_To_FullTypeDateTimeNullable_Using_DynamicLinqType()
308312
{
309313
// Arrange
314+
var config = new ParsingConfig
315+
{
316+
PrioritizePropertyOrFieldOverTheType = true
317+
};
310318
var list = new List<SimpleValuesModel>
311319
{
312320
new SimpleValuesModel { DateTime = DateTime.Now }
313321
};
314322

315323
// Act
316324
var expectedResult = list.Select(x => (DateTime?)x.DateTime);
317-
var result = list.AsQueryable().Select($"\"{typeof(DateTime).FullName}\"?(DateTime)");
325+
var result = list.AsQueryable().Select(config, $"\"{typeof(DateTime).FullName}\"?(DateTime)");
318326

319327
// Assert
320328
Assert.Equal(expectedResult.ToArray(), result.ToDynamicArray<DateTime?>());
@@ -1502,7 +1510,8 @@ public void ExpressionTests_MethodCall_GenericExtension()
15021510
{
15031511
var config = new ParsingConfig
15041512
{
1505-
CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForStaticTesting()
1513+
CustomTypeProvider = new DefaultDynamicLinqCustomTypeProviderForStaticTesting(),
1514+
PrioritizePropertyOrFieldOverTheType = true
15061515
};
15071516

15081517
// Arrange

test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.TypeAccess.cs

+15
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,21 @@ public void ParseTypeAccess_Via_Constructor_String_To_DateTime_Valid()
5353
expression.ToString().Should().NotBeEmpty();
5454
}
5555

56+
[Fact]
57+
public void ParseTypeAccess_Via_Constructor_Arguments_To_DateTime_Valid()
58+
{
59+
// Arrange
60+
var arguments = "2022, 10, 31, 9, 15, 11";
61+
var parameter = Expression.Parameter(typeof(DateTime));
62+
63+
// Act
64+
var parser = new ExpressionParser(new[] { parameter }, $"DateTime({arguments})", new object[] { }, ParsingConfig.Default);
65+
var expression = parser.Parse(typeof(DateTime));
66+
67+
// Assert
68+
expression.ToString().Should().NotBeEmpty();
69+
}
70+
5671
[Theory]
5772
[InlineData(null)]
5873
[InlineData("\"abc\"")]

0 commit comments

Comments
 (0)