28

Why do I get an argument exception saying I pass the wrong number of arguments to string.equals method?

I pass THREE arguments and that should be correct. Actually it should throw a compile time error not runtime...

Do you see the error?

var translations = await (from l in context.Languages
                  join t in context.Translations on l.ISO639_ISO3166 equals t.ISO639_ISO3166
                  where string.Equals(l.ApplicationName, applicationName, StringComparison.InvariantCultureIgnoreCase)
                  select new Translation
                  {
                      Key = t.Key,
                      Text = t.Text
                  }).ToListAsync();

UPDATE

Test Name:  GetTranslations
Test FullName:  TaaS.IntegrationTests.Tests.TranslationRepositoryTests.GetTranslations
Test Source:    C:\test\TaaS-WebApplication\TaaS.IntegrationTests\Tests\TranslationRepositoryTests.cs : line 17
Test Outcome:   Failed
Test Duration:  0:00:00,0473367

Result StackTrace:  
at System.Linq.Expressions.Expression.GetMethodBasedBinaryOperator(ExpressionType binaryType, Expression left, Expression right, MethodInfo method, Boolean liftToNull)
   at System.Linq.Expressions.Expression.Equal(Expression left, Expression right, Boolean liftToNull, MethodInfo method)
   at System.Data.Entity.Core.Objects.ELinq.LinqExpressionNormalizer.VisitMethodCall(MethodCallExpression m)
   at System.Linq.Expressions.EntityExpressionVisitor.Visit(Expression exp)
   at System.Linq.Expressions.EntityExpressionVisitor.VisitLambda(LambdaExpression lambda)
   at System.Linq.Expressions.EntityExpressionVisitor.Visit(Expression exp)
   at System.Linq.Expressions.EntityExpressionVisitor.VisitUnary(UnaryExpression u)
   at System.Linq.Expressions.EntityExpressionVisitor.Visit(Expression exp)
   at System.Linq.Expressions.EntityExpressionVisitor.VisitExpressionList(ReadOnlyCollection`1 original)
   at System.Linq.Expressions.EntityExpressionVisitor.VisitMethodCall(MethodCallExpression m)
   at System.Data.Entity.Core.Objects.ELinq.LinqExpressionNormalizer.VisitMethodCall(MethodCallExpression m)
   at System.Linq.Expressions.EntityExpressionVisitor.Visit(Expression exp)
   at System.Linq.Expressions.EntityExpressionVisitor.VisitExpressionList(ReadOnlyCollection`1 original)
   at System.Linq.Expressions.EntityExpressionVisitor.VisitMethodCall(MethodCallExpression m)
   at System.Data.Entity.Core.Objects.ELinq.LinqExpressionNormalizer.VisitMethodCall(MethodCallExpression m)
   at System.Linq.Expressions.EntityExpressionVisitor.Visit(Expression exp)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter..ctor(Funcletizer funcletizer, Expression expression)
   at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.CreateExpressionConverter()
   at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClassc.<GetResultsAsync>b__a()
   at System.Data.Entity.Core.Objects.ObjectContext.<ExecuteInTransactionAsync>d__3d`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<ExecuteAsyncImplementation>d__9`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Data.Entity.Utilities.TaskExtensions.CultureAwaiter`1.GetResult()
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<GetResultsAsync>d__e.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Data.Entity.Utilities.TaskExtensions.CultureAwaiter`1.GetResult()
   at System.Data.Entity.Internal.LazyAsyncEnumerator`1.<FirstMoveNextAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Data.Entity.Infrastructure.IDbAsyncEnumerableExtensions.<ForEachAsync>d__5`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at TaaS.Repository.TranslationRepository.<GetTranslationsAsync>d__2.MoveNext() in C:\_REPOSITORIES\taas-application\TaaS-WebApplication\TaaS.Repository\TranslationRepository.cs:line 20
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at TaaS.IntegrationTests.Tests.TranslationRepositoryTests.GetTranslations() in C:\_REPOSITORIES\taas-application\TaaS-WebApplication\TaaS.IntegrationTests\Tests\TranslationRepositoryTests.cs:line 45
Result Message: 
Test method TaaS.IntegrationTests.Tests.TranslationRepositoryTests.GetTranslations threw exception: 
System.ArgumentException: Incorrect number of arguments supplied for call to method 'Boolean Equals(System.String, System.String, System.StringComparison)'
Elisabeth
  • 20,496
  • 52
  • 200
  • 321

5 Answers5

31

First of all, SQL string comparisons are case-insensitive, or rather, the most common collations are case-insensitive.

You shouldn't need to use String.Equals at all. Try executing your query without the String.Equals call.

If, for some reason, the query fails to return results, there may be a problem with the arguments or the data. You should try to execute the equivalent SQL statement directly and check whether there are any matching results.

String case would be an issue only if the underlying column's collation was modified to a string-sensitive one. This would be very unusual. In fact, the difficult part is getting LINQ to EF to do a case-sensitive query.

As for the error itself, it's caused because String.Equals can't be translated to SQL. LINQ by itself doesn't execute queries, it's just a language. LINQ providers are responsible for translating queries to the underlying language and executing them.

Some providers, like LINQ to SQL, will parse whatever they can, load the result in memory and pass it to LINQ to Object for the unsupported operations. This typically results in very bad performance. In your case, your query would load all translations in memory and then try to filter them.

LINQ to EF on the other hand doesn't allow this to prevent performance problems. Queries that can't be translated, aren't executed. String.Equals in particular can't be translated to SQL as string comparisons are controlled by culture-specific collations. There is no equivalent to the Invariant Culture.

If your table is case-sensitive, you'll have to change the collation used for comparisons, eg to Latin1_CI_AS. This SO question describes various ways to do that

Community
  • 1
  • 1
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • 2
    Thanks for the case-insensitive comment. That really helped! – Elisabeth Sep 22 '15 at 12:54
  • 1
    Yes, I think this is correct. However, I find it slightly annoying. Code First should not be making so many assumptions about the database that you can put .Where(a == b) as a C# condition and expect it to match where a and b aren't the same case. C# rules should apply regardless. – Stephen Holt Nov 25 '16 at 10:21
  • @StephenHolt it's LINQ to EF, not Code First. Furthermore, it's making *fewer* assumptions precisely to avoid generating unexpectedly bad queries, like LINQ to SQL did. Finally, trying to apply C# rules to a completely different language is a recipe for performance bugs. LINQ isn't C# specific to begin with. – Panagiotis Kanavos Nov 25 '16 at 10:30
  • @StephenHolt In SQL's case, it would be a disaster because collation is *column* specific. It affects *indexing*. Trying to use a different collation invalidates indexes and results in full table scans – Panagiotis Kanavos Nov 25 '16 at 10:31
  • @StephenHolt, since we are talking about translations, different languages have very different matching rules between upper and lower case. The Turkish I is the perfect example - there are [dotted and dotless variants](https://en.wikipedia.org/wiki/Dotted_and_dotless_I) – Panagiotis Kanavos Nov 25 '16 at 10:39
  • @StephenHolt - I wonder if, to achieve what you'd like to see, you could do `.ToList().Where(a == b)`? Haven't tried it, just suggesting. I'd imagine that brings evaluation of `Where` into LINQ to Object. – OutstandingBill Jul 26 '19 at 01:44
  • @OutstandingBill yes, I imagine that would work. By calling ToList() you're bringing the processing of the conditional in-house, and it will be processed using C# rules. It's not necessarily a good thing to do, though, because it will cause the whole set to be loaded from DB into memory before applying the conditional. – Stephen Holt Aug 28 '19 at 12:58
11

This is a runtime error because you are likely running against a Linq query provider, which takes an expression the C# compiler created from your C# code and executes it at runtime. The provider likely can't translate this overload of Equals.

Try changing your Linq query to be:

(from l in context.Languages
join t in context.Translations on l.ISO639_ISO3166 equals t.ISO639_ISO3166).AsEnumerable()
.Where(l => string.Equals(l.ApplicationName, applicationName, StringComparison.InvariantCultureIgnoreCase))
.Select(new Translation
              {
                  Key = t.Key,
                  Text = t.Text
              }).ToListAsync();
Community
  • 1
  • 1
codekaizen
  • 26,990
  • 7
  • 84
  • 140
  • 1
    While this is the correct answer, is there any list of a functions that are required to be supported by **any** Linq query provider? I know about `Canonical Functions`, but AFAIK they are EF specific. Many people get confused when switching from `IEnumerabe` to `IQueryable` - everything happily compiles and then they start getting those runtime errors. – Ivan Stoev Sep 22 '15 at 10:28
  • It depends on the provider. – codekaizen Sep 22 '15 at 10:36
  • I see. So nothing similar to ANSI SQL like ECMA LINQL :-) Thanks. – Ivan Stoev Sep 22 '15 at 10:42
  • @codekaizen copy/paste your code I can not make it compile. – Elisabeth Sep 22 '15 at 11:41
  • 3
    I changed the comparison to "==" because that is what I wanted remembering again that the sql comparison is case insensitive thanks to @Panagiotis Kanavos. – Elisabeth Sep 22 '15 at 12:53
  • This answer doesn't work. The string.Equals mentioned is still the version that is not supported by Entity Framework. – Stephen Holt Nov 25 '16 at 10:19
  • 4
    This is not a good answer because `AsEnumerable()` loads **all** rows in memory, then tries to apply the `WHERE` condition without any benefit of indexing. Instead of loading eg 10 rows from a 100K table, it will load all 100K rows – Panagiotis Kanavos Nov 25 '16 at 10:34
  • @IvanStoev this is not the correct answer, actually it's a common bug - switching to Enumerable executes the query and loads everything in memory. – Panagiotis Kanavos Nov 25 '16 at 10:35
  • @PanagiotisKanavos While you are right in general, looks like EF Core consider loading whole tables in memory not a bug, but feature - it does that by default for any expression it cannot translate to SQL :) – Ivan Stoev Nov 25 '16 at 10:46
  • @IvanStoev source? Because it does no such thing. You can't load data from a database without SQL - SQL Server doesn't listen to anything *except* SQL. You can easily verify this by using SQL Server Profiler. Perhaps you confuse EF Core with the planned (but not released) NoSQL support? – Panagiotis Kanavos Nov 25 '16 at 10:50
  • 1
    @PanagiotisKanavos Sure, I meant the LINQ query not fully translated to SQL like in EF6. But something like the above, only made automatically for you :) See, I'm kidding, but it's really a big problem, and MS team made it a default feature. They call it mixed query execution or something like that. – Ivan Stoev Nov 25 '16 at 10:55
  • While this may work, this is the wrong solution. Since SQL string comparisons are already case insensitive, simply doing a `l.ApplicationName == applicationName` comparison will do a case insensitive compare, because the LINQ expression will just be translated into a SQL statement. The error is happening by DESIGN, don't try to "make it work". – Sergey Jul 25 '19 at 17:41
  • 1
    @Sergey SQL string comparison is not always case insensitive. The database collation determines this. – codekaizen Jul 26 '19 at 17:04
  • Yep, good point @codekaizen. Maybe the answer can be updated to mention that. – Sergey Jul 26 '19 at 18:36
1

In Linq you need to use the static method of equals. Do this instead and it will work:

where l.ApplicationName.Equals(applicationName, StringComparison.InvariantCultureIgnoreCase)
Nacho
  • 153
  • 2
  • 5
0

Another way (if you insist on case insensitive comparison)

var translations = await (from l in context.Languages
                  join t in context.Translations on l.ISO639_ISO3166 equals t.ISO639_ISO3166
                  where l.ApplicationName.Equals(applicationName,StringComparison.CurrentCultureIgnoreCase)
                  select new Translation
                  {
                      Key = t.Key,
                      Text = t.Text
                  }).ToListAsync();

Note:

l.ApplicationName.Equals(applicationName,StringComparison.CurrentCultureIgnoreCase)

But most likely you don' t need this as Linq to SQL is by default

"..case-insensitive, or rather, the most common collations are case-insensitive..."

Vitaliy Markitanov
  • 2,205
  • 1
  • 24
  • 23
0

Just one more way of case-insensitive comparison if you can opt this:

where l.ApplicationName.ToLower() == applicationName

where applicationName can be passed as applicationName.ToLower() so this becomes case-insensitive comparison :P

Deepak
  • 2,660
  • 2
  • 8
  • 23