diff --git a/Microsoft.Azure.Cosmos/src/Linq/SubtreeEvaluator.cs b/Microsoft.Azure.Cosmos/src/Linq/SubtreeEvaluator.cs
index aaaf31d839..8ecb2ea9a2 100644
--- a/Microsoft.Azure.Cosmos/src/Linq/SubtreeEvaluator.cs
+++ b/Microsoft.Azure.Cosmos/src/Linq/SubtreeEvaluator.cs
@@ -45,7 +45,7 @@ protected override Expression VisitMemberInit(MemberInitExpression node)
private Expression EvaluateMemberAccess(Expression expression)
{
- while (expression.CanReduce)
+ while (expression?.CanReduce ?? false)
{
expression = expression.Reduce();
}
@@ -57,7 +57,7 @@ private Expression EvaluateMemberAccess(Expression expression)
// This is done because the compilation of a delegate takes a global lock which causes highly
// threaded clients to exhibit async-over-sync thread exhaustion behaviour on this call path
// even when doing relatively straightforward queries.
- if (!(expression is MemberExpression memberExpression))
+ if (expression is not MemberExpression memberExpression)
{
return expression;
}
@@ -66,19 +66,26 @@ private Expression EvaluateMemberAccess(Expression expression)
// nested property access (x.y.z) without needing to fall back on delegate compilation.
Expression targetExpression = this.EvaluateMemberAccess(memberExpression.Expression);
- if (!(targetExpression is ConstantExpression targetConstant))
+ // NOTE: When evaluating static field or property access, we may have a null targetExpression.
+ // In this situation, we should pass the null value to the GetValue(...) methods below to
+ // indicate that we are accessing a static member.
+ ConstantExpression targetConstant = targetExpression as ConstantExpression;
+
+ // If we have a target expression but it cannot be resolved to a constant, then we should skip
+ // using reflectoin here and instead rely on the fallback delegate compilation approach.
+ if (targetExpression is not null && targetConstant is null)
{
return expression;
}
if (memberExpression.Member is FieldInfo fieldInfo)
{
- return Expression.Constant(fieldInfo.GetValue(targetConstant.Value), memberExpression.Type);
+ return Expression.Constant(fieldInfo.GetValue(targetConstant?.Value), memberExpression.Type);
}
if (memberExpression.Member is PropertyInfo propertyInfo)
{
- return Expression.Constant(propertyInfo.GetValue(targetConstant.Value), memberExpression.Type);
+ return Expression.Constant(propertyInfo.GetValue(targetConstant?.Value), memberExpression.Type);
}
return expression;
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestMemberAccess.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestMemberAccess.xml
index 9b5974fe37..820a10efd1 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestMemberAccess.xml
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestMemberAccess.xml
@@ -35,4 +35,40 @@ FROM root
WHERE (root["NumericField"] = 3)]]>
+
+
+
+ (doc.NumericField == AmbientContextObject.StaticFieldAccess))]]>
+
+
+
+
+
+
+ (doc.NumericField == AmbientContextObject.StaticPropertyAccess))]]>
+
+
+
+
+
+
+ (doc.NumericField == 6))]]>
+
+
+
\ No newline at end of file
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/LinqTranslationBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/LinqTranslationBaselineTests.cs
index a2eff4baa2..a3b62a1ff1 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/LinqTranslationBaselineTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/LinqTranslationBaselineTests.cs
@@ -151,7 +151,16 @@ internal class AmbientContextObject
public double PropertyAccess { get; set; }
- public double MethodAccess() => 1.0;
+ public double MethodAccess()
+ {
+ return 1.0;
+ }
+
+ public static double StaticFieldAccess = 4.0;
+
+ public static double StaticPropertyAccess => 5.0;
+
+ public const double ConstAccess = 6.0;
}
[TestMethod]
@@ -560,6 +569,9 @@ public void TestMemberAccess()
// performance boost, especially under highly concurrent workloads).
new LinqTestInput("Filter on Field value", b => getQuery(b).Where(doc => doc.NumericField == ambientContext.FieldAccess)),
new LinqTestInput("Filter on Property value", b => getQuery(b).Where(doc => doc.NumericField == ambientContext.PropertyAccess)),
+ new LinqTestInput("Filter on Static Field value", b => getQuery(b).Where(doc => doc.NumericField == AmbientContextObject.StaticFieldAccess)),
+ new LinqTestInput("Filter on Static Property value", b => getQuery(b).Where(doc => doc.NumericField == AmbientContextObject.StaticPropertyAccess)),
+ new LinqTestInput("Filter on Const value", b => getQuery(b).Where(doc => doc.NumericField == AmbientContextObject.ConstAccess)),
};
this.ExecuteTestSuite(inputs);
}