diff --git a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs index 1dddb75e..eb8e52f5 100644 --- a/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs +++ b/src/System.Linq.Dynamic.Core/Parser/ExpressionParser.cs @@ -1920,11 +1920,12 @@ private bool TryFindPropertyOrField(Type type, string id, Expression? expression switch (member) { case PropertyInfo property: - propertyOrFieldExpression = Expression.Property(expression, property); + var propertyIsStatic = property?.GetGetMethod().IsStatic ?? property?.GetSetMethod().IsStatic ?? false; + propertyOrFieldExpression = propertyIsStatic ? Expression.Property(null, property) : Expression.Property(expression, property); return true; case FieldInfo field: - propertyOrFieldExpression = Expression.Field(expression, field); + propertyOrFieldExpression = field.IsStatic ? Expression.Field(null, field) : Expression.Field(expression, field); return true; default: @@ -2478,7 +2479,7 @@ private static Exception IncompatibleOperandsError(string opName, Expression lef private MemberInfo? FindPropertyOrField(Type type, string memberName, bool staticAccess) { #if !(UAP10_0 || NETSTANDARD) - var extraBindingFlag = _parsingConfig.PrioritizePropertyOrFieldOverTheType && staticAccess ? BindingFlags.Static : BindingFlags.Instance; + var extraBindingFlag = _parsingConfig.PrioritizePropertyOrFieldOverTheType && staticAccess ? BindingFlags.Static : BindingFlags.Instance | BindingFlags.Static; var bindingFlags = BindingFlags.Public | BindingFlags.DeclaredOnly | extraBindingFlag; foreach (Type t in TypeHelper.GetSelfAndBaseTypes(type)) { @@ -2492,11 +2493,12 @@ private static Exception IncompatibleOperandsError(string opName, Expression lef } return null; #else - var isCaseSensitive = _parsingConfig?.IsCaseSensitive == true; + var isCaseSensitive = _parsingConfig.IsCaseSensitive == true; foreach (Type t in TypeHelper.GetSelfAndBaseTypes(type)) { // Try to find a property with the specified memberName - MemberInfo? member = t.GetTypeInfo().DeclaredProperties.FirstOrDefault(x => (!staticAccess || x.GetAccessors(true)[0].IsStatic) && ((x.Name == memberName) || (!isCaseSensitive && x.Name.Equals(memberName, StringComparison.OrdinalIgnoreCase)))); + MemberInfo? member = t.GetTypeInfo().DeclaredProperties + .FirstOrDefault(x => (!staticAccess || x.GetAccessors(true)[0].IsStatic) && (x.Name == memberName || (!isCaseSensitive && x.Name.Equals(memberName, StringComparison.OrdinalIgnoreCase)))); if (member != null) { return member; diff --git a/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.MemberAccess.cs b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.MemberAccess.cs index 9d60381b..f4eef1bf 100644 --- a/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.MemberAccess.cs +++ b/test/System.Linq.Dynamic.Core.Tests/Parser/ExpressionParserTests.MemberAccess.cs @@ -1,4 +1,5 @@ -using FluentAssertions; +using System.Linq.Dynamic.Core.CustomTypeProviders; +using FluentAssertions; using Xunit; namespace System.Linq.Dynamic.Core.Tests.Parser @@ -21,6 +22,67 @@ public void ParseMemberAccess_DictionaryIndex_On_Dynamic() expression.ToString().Should().Be("System.Linq.Dynamic.Core.Tests.Parser.ProductDynamic[].Where(Param_0 => ([Dynamic] == Convert(\"First Product\", Object)))"); #endif } + + [Theory] + [InlineData("Prop", "TestProp")] + [InlineData("Field", "TestField")] + public void Parse_StaticPropertyOrField_In_StaticClass1(string name, string value) + { + // Arrange + var queryable = new int[1].AsQueryable(); + + // Act + var result = queryable.Select($"{typeof(StaticClassExample)}.{name}").First(); + + // Assert + Assert.Equal(value, result); + } + + [Theory] + [InlineData("Prop", "TestProp")] + [InlineData("Field", "TestField")] + public void Parse_StaticPropertyOrField_In_NonStaticClass1(string name, string value) + { + // Arrange + var queryable = new int[1].AsQueryable(); + + // Act + var result = queryable.Select($"new {typeof(NonStaticClassExample)}().{name}").First(); + + // Assert + Assert.Equal(value, result); + } + + [Theory] + [InlineData("Prop", "TestProp")] + [InlineData("Field", "TestField")] + public void Parse_StaticPropertyOrField_In_NonStaticClass2(string name, string value) + { + // Arrange + var queryable = new[] { new NonStaticClassExample() }.AsQueryable(); + + // Act + var result = queryable.Select(name).First(); + + // Assert + Assert.Equal(value, result); + } + } + + [DynamicLinqType] + public class StaticClassExample + { + public static string Prop { get; set; } = "TestProp"; + + public static string Field = "TestField"; + } + + [DynamicLinqType] + public class NonStaticClassExample + { + public static string Prop { get; set; } = "TestProp"; + + public static string Field = "TestField"; } public class ProductDynamic diff --git a/test/System.Linq.Dynamic.Core.Tests/SecurityTests.cs b/test/System.Linq.Dynamic.Core.Tests/SecurityTests.cs index a16f259a..787124e3 100644 --- a/test/System.Linq.Dynamic.Core.Tests/SecurityTests.cs +++ b/test/System.Linq.Dynamic.Core.Tests/SecurityTests.cs @@ -145,7 +145,7 @@ public void UsingStaticClassAsType_WhenAddedToDefaultDynamicLinqCustomTypeProvid action.Should().NotThrow(); } - [Theory(Skip = "873")] + [Theory] [InlineData("new System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings3()", "SettingsProp[\"jwt\"]")] [InlineData("new System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings3()", "SettingsField[\"jwt\"]")] [InlineData("c => new System.Linq.Dynamic.Core.Tests.Helpers.Models.AppSettings3()", "SettingsProp[\"jwt\"]")]