Skip to content

Commit b98fc43

Browse files
rstamBorisDog
authored andcommitted
CSHARP-4100: Some String methods that are defined in .NET Core 2.1 but not in .NET Standard 2.0 have to be handled using reflection.
1 parent 2ac2bad commit b98fc43

File tree

5 files changed

+254
-60
lines changed

5 files changed

+254
-60
lines changed

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/ContainsMethodToAggregationExpressionTranslator.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515

1616
using System.Linq.Expressions;
17+
using MongoDB.Bson.Serialization;
1718
using MongoDB.Bson.Serialization.Serializers;
1819
using MongoDB.Driver.Linq.Linq3Implementation.Ast.Expressions;
1920
using MongoDB.Driver.Linq.Linq3Implementation.Misc;
@@ -56,9 +57,15 @@ private static bool IsEnumerableContainsMethod(MethodCallExpression expression,
5657
{
5758
sourceExpression = expression.Object;
5859
valueExpression = arguments[0];
59-
var ienumerableInterface = sourceExpression.Type.GetIEnumerableGenericInterface();
60-
var itemType = ienumerableInterface.GetGenericArguments()[0];
61-
return itemType == valueExpression.Type;
60+
if (sourceExpression.Type.TryGetIEnumerableGenericInterface(out var ienumerableInterface))
61+
{
62+
var itemType = ienumerableInterface.GetGenericArguments()[0];
63+
if (itemType == valueExpression.Type)
64+
{
65+
// string.Contains(char) is not translated like other Contains methods because string is not represented as an array
66+
return sourceExpression.Type != typeof(string) && valueExpression.Type != typeof(char);
67+
}
68+
}
6269
}
6370

6471
sourceExpression = null;

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToAggregationExpressionTranslators/MethodTranslators/StartsWithContainsOrEndsWithMethodToAggregationExpressionTranslator.cs

Lines changed: 111 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,72 @@ public static bool CanTranslate(MethodCallExpression expression)
7272
{
7373
var method = expression.Method;
7474

75+
if (method.IsOneOf(__startsWithContainsOrEndsWithMethods))
76+
{
77+
return true;
78+
}
79+
80+
// on .NET Framework string.Contains(char) compiles to Enumerable.Contains<char>(string, char)
81+
// on all frameworks we will translate Enumerable.Contains<char>(string, char) the same as string.Contains(char)
7582
if (method.Is(EnumerableMethod.Contains) && expression.Arguments[0].Type == typeof(string))
7683
{
7784
return true;
7885
}
7986

80-
return method.IsOneOf(__startsWithContainsOrEndsWithMethods);
87+
#if NETSTANDARD2_0
88+
// some String methods are defined in .NET Core 2.1 but not in .NET Standard 2.0 so we have to identify them using reflection in .NET Standard 2.0
89+
if (method.DeclaringType == typeof(string) && !method.IsStatic)
90+
{
91+
var parameters = method.GetParameters();
92+
switch (method.Name)
93+
{
94+
case "Contains":
95+
switch (parameters.Length)
96+
{
97+
case 1:
98+
if (parameters[0].ParameterType == typeof(char))
99+
{
100+
return true;
101+
}
102+
break;
103+
104+
case 2:
105+
if (parameters[0].ParameterType == typeof(char) &&
106+
parameters[1].ParameterType == typeof(StringComparison))
107+
{
108+
return true;
109+
}
110+
if (parameters[0].ParameterType == typeof(string) &&
111+
parameters[1].ParameterType == typeof(StringComparison))
112+
{
113+
return true;
114+
}
115+
if (parameters[0].ParameterType == typeof(string) &&
116+
parameters[1].ParameterType == typeof(CultureInfo))
117+
{
118+
return true;
119+
}
120+
break;
121+
}
122+
break;
123+
124+
case "EndsWith":
125+
case "StartsWith":
126+
switch (parameters.Length)
127+
{
128+
case 1:
129+
if (parameters[0].ParameterType == typeof(char))
130+
{
131+
return true;
132+
}
133+
break;
134+
}
135+
break;
136+
}
137+
}
138+
#endif
139+
140+
return false;
81141
}
82142

83143
public static AggregationExpression Translate(TranslationContext context, MethodCallExpression expression)
@@ -123,12 +183,12 @@ public static AggregationExpression Translate(TranslationContext context, Method
123183
}
124184
}
125185
bool ignoreCase = false;
126-
if (method.IsOneOf(__withComparisonTypeMethods))
186+
if (IsWithComparisonTypeMethod(method))
127187
{
128188
var comparisonTypeExpression = arguments[1];
129189
ignoreCase = GetIgnoreCaseFromComparisonType(comparisonTypeExpression);
130190
}
131-
if (method.IsOneOf(__withIgnoreCaseAndCultureMethods))
191+
if (IsWithIgnoreCaseAndCultureMethod(method))
132192
{
133193
var ignoreCaseExpression = arguments[1];
134194
var cultureExpression = arguments[2];
@@ -206,6 +266,54 @@ bool GetIgnoreCaseFromIgnoreCaseAndCulture(Expression ignoreCaseExpression, Expr
206266

207267
return ignoreCase;
208268
}
269+
270+
bool IsWithComparisonTypeMethod(MethodInfo method)
271+
{
272+
if (method.IsOneOf(__withComparisonTypeMethods))
273+
{
274+
return true;
275+
}
276+
277+
#if NETSTANDARD2_0
278+
// some String methods are defined in .NET Core 2.1 but not in .NET Standard 2.0 so we have to identify them using reflection in .NET Standard 2.0
279+
var parameters = method.GetParameters();
280+
if (parameters.Length > 0)
281+
{
282+
var lastParameter = parameters[parameters.Length - 1];
283+
if (lastParameter.ParameterType == typeof(StringComparison))
284+
{
285+
return true;
286+
}
287+
}
288+
#endif
289+
290+
return false;
291+
}
292+
293+
bool IsWithIgnoreCaseAndCultureMethod(MethodInfo method)
294+
{
295+
if (method.IsOneOf(__withIgnoreCaseAndCultureMethods))
296+
{
297+
return true;
298+
}
299+
300+
#if NETSTANDARD2_0
301+
// some String methods are defined in .NET Core 2.1 but not in .NET Standard 2.0 so we have to identify them using reflection in .NET Standard 2.0
302+
var parameters = method.GetParameters();
303+
if (parameters.Length > 2)
304+
{
305+
var nextToLastParameter = parameters[parameters.Length - 2];
306+
var lastParameter = parameters[parameters.Length - 1];
307+
if (nextToLastParameter.ParameterType == typeof(bool) &&
308+
lastParameter.ParameterType == typeof(CultureInfo))
309+
{
310+
return true;
311+
}
312+
}
313+
#endif
314+
315+
return false;
316+
}
209317
}
210318
}
211319
}

src/MongoDB.Driver/Linq/Linq3Implementation/Translators/ExpressionToFilterTranslators/ExpressionTranslators/StringExpressionToRegexFilterTranslator.cs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,65 @@ public static bool CanTranslate(Expression expression)
166166
{
167167
var method = methodCallExpression.Method;
168168

169+
if (method.IsOneOf(__translatableMethods))
170+
{
171+
return true;
172+
}
173+
174+
// on .NET Framework string.Contains(char) compiles to Enumerable.Contains<char>(string, char)
175+
// on all frameworks we will translate Enumerable.Contains<char>(string, char) the same as string.Contains(char)
169176
if (method.Is(EnumerableMethod.Contains) && methodCallExpression.Arguments[0].Type == typeof(string))
170177
{
171178
return true;
172179
}
173180

174-
return method.IsOneOf(__translatableMethods);
181+
#if NETSTANDARD2_0
182+
// some String methods are defined in .NET Core 2.1 but not in .NET Standard 2.0 so we have to identify them using reflection in .NET Standard 2.0
183+
if (method.DeclaringType == typeof(string) && !method.IsStatic)
184+
{
185+
var parameters = method.GetParameters();
186+
switch (method.Name)
187+
{
188+
case "Contains":
189+
switch (parameters.Length)
190+
{
191+
case 1:
192+
if (parameters[0].ParameterType == typeof(char))
193+
{
194+
return true;
195+
}
196+
break;
197+
198+
case 2:
199+
if (parameters[0].ParameterType == typeof(char) &&
200+
parameters[1].ParameterType == typeof(StringComparison))
201+
{
202+
return true;
203+
}
204+
if (parameters[0].ParameterType == typeof(string) &&
205+
parameters[1].ParameterType == typeof(StringComparison))
206+
{
207+
return true;
208+
}
209+
break;
210+
}
211+
break;
212+
213+
case "EndsWith":
214+
case "StartsWith":
215+
switch (parameters.Length)
216+
{
217+
case 1:
218+
if (parameters[0].ParameterType == typeof(char))
219+
{
220+
return true;
221+
}
222+
break;
223+
}
224+
break;
225+
}
226+
}
227+
#endif
175228
}
176229

177230
return false;
@@ -360,6 +413,32 @@ leftMemberExpression.Member is PropertyInfo propertyInfo &&
360413
propertyInfo.Is(StringProperty.Length);
361414
}
362415

416+
private static bool IsWithComparisonTypeMethod(MethodInfo method)
417+
{
418+
if (method.IsOneOf(__withComparisonTypeMethods))
419+
{
420+
return true;
421+
}
422+
423+
#if NETSTANDARD2_0
424+
// some String methods are defined in .NET Core 2.1 but not in .NET Standard 2.0 so we have to identify them using reflection in .NET Standard 2.0
425+
if (method.DeclaringType == typeof(string) && !method.IsStatic)
426+
{
427+
var parameters = method.GetParameters();
428+
if (parameters.Length > 0)
429+
{
430+
var lastParameter = parameters[parameters.Length - 1];
431+
if (lastParameter.ParameterType == typeof(StringComparison))
432+
{
433+
return true;
434+
}
435+
}
436+
}
437+
#endif
438+
439+
return false;
440+
}
441+
363442
private static Modifiers TranslateComparisonType(Modifiers modifiers, Expression expression, Expression comparisonTypeExpression)
364443
{
365444
var comparisonType = comparisonTypeExpression.GetConstantValue<StringComparison>(containingExpression: expression);
@@ -491,7 +570,7 @@ private static AstFilter TranslateStartsWithOrContainsOrEndsWith(TranslationCont
491570
value = valueExpression.GetConstantValue<string>(containingExpression: expression);
492571
}
493572

494-
if (method.IsOneOf(__withComparisonTypeMethods))
573+
if (IsWithComparisonTypeMethod(method))
495574
{
496575
modifiers = TranslateComparisonType(modifiers, expression, arguments[1]);
497576
}

0 commit comments

Comments
 (0)