-
Notifications
You must be signed in to change notification settings - Fork 933
Evaluate parameter expression for Enumerable.Contains calls #2873
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
18b021d
c422fb9
654166d
57476c4
ed4f6ac
c5bbd0f
11a906a
1704521
1ff1853
9f93541
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,8 +16,10 @@ | |
// | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Linq.Expressions; | ||
using System.Reflection; | ||
using NHibernate.Collection; | ||
using NHibernate.Engine; | ||
using NHibernate.Linq.Functions; | ||
|
@@ -61,6 +63,7 @@ public static Expression EvaluateIndependentSubtrees( | |
// _partialEvaluationInfo contains a list of the expressions that are safe to be evaluated. | ||
private readonly PartialEvaluationInfo _partialEvaluationInfo; | ||
private readonly PreTransformationParameters _preTransformationParameters; | ||
private static readonly MethodInfo ContainsMethodInfo = ReflectHelper.FastGetMethodDefinition(Enumerable.Contains, default(IEnumerable<object>), default(object)); | ||
|
||
private NhPartialEvaluatingExpressionVisitor( | ||
PartialEvaluationInfo partialEvaluationInfo, | ||
|
@@ -155,6 +158,42 @@ private Expression EvaluateSubtree(Expression subtree) | |
|
||
#region NH additions | ||
|
||
protected override Expression VisitMethodCall(MethodCallExpression node) | ||
{ | ||
DetectEvaluatableExpressionOnCollectionContains(node); | ||
return base.VisitMethodCall(node); | ||
} | ||
|
||
private void DetectEvaluatableExpressionOnCollectionContains(MethodCallExpression expression) | ||
{ | ||
if (!expression.Method.IsGenericMethod || ContainsMethodInfo != expression.Method.GetGenericMethodDefinition()) | ||
return; | ||
var argument = expression.Arguments[0]; | ||
if (argument.NodeType != ExpressionType.Call) | ||
return; | ||
|
||
if(TryGetCollectionParameter((MethodCallExpression)argument)) | ||
_partialEvaluationInfo.AddEvaluatableExpression(argument); | ||
} | ||
|
||
private bool TryGetCollectionParameter(MethodCallExpression expression) | ||
{ | ||
if (expression.Method.DeclaringType != typeof(Enumerable)) | ||
return IsCollectionParameter(expression); | ||
|
||
var arg = expression.Arguments[0]; | ||
|
||
if (IsCollectionParameter(arg)) | ||
return true; | ||
|
||
return arg.NodeType == ExpressionType.Call && TryGetCollectionParameter((MethodCallExpression) arg); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need to do that recursively? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To support chain of LINQ commands. And to make sure it doesn't start with mapped association (somethin like |
||
} | ||
|
||
private bool IsCollectionParameter(Expression expression) | ||
{ | ||
return _partialEvaluationInfo.IsEvaluatableExpression(expression); | ||
} | ||
|
||
private bool ContainsVariable(Expression expression) | ||
{ | ||
if (!(expression is UnaryExpression unaryExpression) || | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here you're modifying external dependency, which you should not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't get it. Why I shouldn't touch it? It's just a wrapper around set of expressions that needs evaluation. I can store it in separate field (as I did initially) - but to minimize change noise I used already present class for storing evaluatable expressions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because this is the information provided by another concerning party. We already let this logic spread across solution and so we're trying to catch bugs doing 10th bugfix release. Let's avoid doing ad-hoc patches of logic here and there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well.. Ok... It can be stored separately. But I still think it's more proper to store it in
_partialEvaluationInfo
- external or not, it stores list of expressions that needs evaluation. So it's better to fill it also with our expressions - so all other external parties can handle it appropriately as evaluatable expression.But before making any further changes - do you agree with this fix on principle?