Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,29 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using MongoDB.Bson.Serialization;
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
using MongoDB.Driver.Linq.Linq3Implementation.Reflection;

namespace MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods
{
internal static class ExpressionExtensions
{
private readonly static MethodInfo[] __nonDeterministicMethods =
[
GuidMethod.NewGuid,
RandomMethod.Next
];

private readonly static PropertyInfo[] __nonDeterministicProperties =
[
DateTimeProperty.Now,
DateTimeProperty.Today,
DateTimeProperty.UtcNow,
DateTimeOffsetProperty.Now,
DateTimeOffsetProperty.UtcNow
];
public static object Evaluate(this Expression expression)
{
if (expression is ConstantExpression constantExpression)
Expand Down Expand Up @@ -60,5 +77,20 @@ public static TValue GetConstantValue<TValue>(this Expression expression, Expres
var message = $"Expression must be a constant: {expression} in {containingExpression}.";
throw new ExpressionNotSupportedException(message);
}

public static bool IsNonDeterministic(this Expression expression)
{
if (expression is MemberExpression memberExpression &&
memberExpression.Member is PropertyInfo propertyInfo)
{
return propertyInfo.IsOneOf(__nonDeterministicProperties);
}
else if (expression is MethodCallExpression methodCallExpression)
{
return methodCallExpression.Method.IsOneOf(__nonDeterministicMethods);
}

return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods;
using MongoDB.Driver.Linq.Linq3Implementation.Reflection;

namespace MongoDB.Driver.Linq.Linq3Implementation.Misc
Expand Down Expand Up @@ -207,6 +208,18 @@ protected override Expression VisitListInit(ListInitExpression node)
return node;
}

protected override Expression VisitMember(MemberExpression node)
{
var result = base.VisitMember(node);

if (result.IsNonDeterministic())
{
_cannotBeEvaluated = true;
}

return result;
}

protected override Expression VisitMemberInit(MemberInitExpression node)
{
// Bindings must be visited before NewExpression
Expand All @@ -231,7 +244,8 @@ protected override Expression VisitMethodCall(MethodCallExpression node)

var method = node.Method;
if (IsCustomLinqExtensionMethod(method) ||
method.Is(QueryableMethod.AsQueryable))
method.Is(QueryableMethod.AsQueryable) ||
result.IsNonDeterministic())
{
_cannotBeEvaluated = true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using System.Reflection;

namespace MongoDB.Driver.Linq.Linq3Implementation.Reflection
{
internal static class DateTimeOffsetProperty
{
// private static fields
private static readonly PropertyInfo __now;
private static readonly PropertyInfo __utcNow;

// static constructor
static DateTimeOffsetProperty()
{
__now = ReflectionInfo.Property(() => DateTimeOffset.Now);
__utcNow = ReflectionInfo.Property(() => DateTimeOffset.UtcNow);
}

// public properties
public static PropertyInfo Now => __now;
public static PropertyInfo UtcNow => __utcNow;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using System.Reflection;

namespace MongoDB.Driver.Linq.Linq3Implementation.Reflection
{
internal static class DateTimeProperty
{
// private static fields
private static readonly PropertyInfo __now;
private static readonly PropertyInfo __today;
private static readonly PropertyInfo __utcNow;

// static constructor
static DateTimeProperty()
{
__now = ReflectionInfo.Property(() => DateTime.Now);
__today = ReflectionInfo.Property(() => DateTime.Today);
__utcNow = ReflectionInfo.Property(() => DateTime.UtcNow);
}

// public properties
public static PropertyInfo Now => __now;
public static PropertyInfo Today => __today;
public static PropertyInfo UtcNow => __utcNow;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using System.Reflection;

namespace MongoDB.Driver.Linq.Linq3Implementation.Reflection
{
internal static class GuidMethod
{
// private static fields
private static readonly MethodInfo __newGuid;

// static constructor
static GuidMethod()
{
__newGuid = ReflectionInfo.Method(() => Guid.NewGuid());
}

// public properties
public static MethodInfo NewGuid => __newGuid;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* Copyright 2010-present MongoDB Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using System.Reflection;

namespace MongoDB.Driver.Linq.Linq3Implementation.Reflection
{
internal static class RandomMethod
{
// private static fields
private static readonly MethodInfo __next;

// static constructor
static RandomMethod()
{
__next = ReflectionInfo.Method((Random random) => random.Next());
}

// public properties
public static MethodInfo Next => __next;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ public static ConstructorInfo Constructor<T1, T2, T3, T4, T5, T6, T7, TObject>(E
return ExtractConstructorInfoFromLambda(lambda);
}

public static MethodInfo Method<TResult>(Expression<Func<TResult>> lambda)
{
return ExtractMethodInfoFromLambda(lambda);
}

public static MethodInfo Method<T1, TResult>(Expression<Func<T1, TResult>> lambda)
{
return ExtractMethodInfoFromLambda(lambda);
Expand Down Expand Up @@ -102,6 +107,11 @@ public static MethodInfo Method<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(Express
return ExtractMethodInfoFromLambda(lambda);
}

public static PropertyInfo Property<TProperty>(Expression<Func<TProperty>> lambda)
{
return ExtractPropertyInfoFromLambda(lambda);
}

public static PropertyInfo Property<TObject, TProperty>(Expression<Func<TObject, TProperty>> lambda)
{
return ExtractPropertyInfoFromLambda(lambda);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
using MongoDB.Bson.Serialization.Options;
using MongoDB.Bson.Serialization.Serializers;
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions;
using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods;
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
using MongoDB.Driver.Linq.Linq3Implementation.Reflection;
using MongoDB.Driver.Linq.Linq3Implementation.Serializers;
Expand All @@ -36,6 +37,20 @@ public static TranslatedExpression Translate(TranslationContext context, MemberE
{
var containerExpression = expression.Expression;
var member = expression.Member;
var declaringType = expression.Member.DeclaringType;
var memberName = expression.Member.Name;

if (containerExpression == null)
{
// TODO: add support for static properties here

if (expression.IsNonDeterministic())
{
throw new ExpressionNotSupportedException(expression, because: $"non-deterministic field or property '{declaringType.Name}.{memberName}' should not be evaluated client-side and is not currently supported server-side");
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For non-deterministic expressions give a better error message saying why it isn't supported.


throw new ExpressionNotSupportedException(expression);
}

if (member is PropertyInfo property && property.DeclaringType.IsNullable())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*/

using System.Linq.Expressions;
using MongoDB.Driver.Linq.Linq3Implementation.ExtensionMethods;
using MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators.MethodTranslators;

namespace MongoDB.Driver.Linq.Linq3Implementation.Translators.ExpressionToAggregationExpressionTranslators
Expand All @@ -22,7 +23,10 @@ internal static class MethodCallExpressionToAggregationExpressionTranslator
{
public static TranslatedExpression Translate(TranslationContext context, MethodCallExpression expression)
{
switch (expression.Method.Name)
var method = expression.Method;
var declaringType = method.DeclaringType;

switch (method.Name)
{
case "Abs": return AbsMethodToAggregationExpressionTranslator.Translate(context, expression);
case "Add": return AddMethodToAggregationExpressionTranslator.Translate(context, expression);
Expand Down Expand Up @@ -209,6 +213,11 @@ public static TranslatedExpression Translate(TranslationContext context, MethodC
return TrimMethodToAggregationExpressionTranslator.Translate(context, expression);
}

if (expression.IsNonDeterministic())
{
throw new ExpressionNotSupportedException(expression, because: $"non-deterministic method '{declaringType.Name}.{method.Name}' should not be evaluated client-side and is not currently supported server-side");
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For non-deterministic expressions give a better error message saying why it isn't supported.


throw new ExpressionNotSupportedException(expression);
}
}
Expand Down
Loading