EntityFramework uses its own logic instead of method in Where clause

I stumbled upon a interesting behavior when using Compare in a C# project that uses EntityFramework.

When using a method with the signature Compare(byte[], byte[]), EntityFramework does not use this method but does a comparison in the SQL Query.
My expectation is that it should be evaluated locally (and throw an exception based on the configuration)

public class MyEntity
{
  ...

  [Required]
  [ConcurrencyCheck]
  public byte[] Timestamp { get; set; }
}


public static int Compare(this byte[] left, byte[] right)
{
  throw new InvalidOperationException("...");
}

byte[] someValue = ...;
MyEntity[] items = context.MyEntities
    .Where(e => e.Timestamp.Compare(someValue) <= 0)
    .ToArray();

This code does not throw the exception but does generate a SQL query like this:

SELECT .....
FROM [MyEntities] AS [f]
WHERE [f].[Timestamp] <= @__someValue_0

When renaming the method this does no longer work and the expected exception is thrown.

This also works when using it directly without an extension method.

public static class MyExtensions
{
  public static int Compare(byte[] left, byte[] right)
  {
    throw new InvalidOperationException("...");
  }
}

byte[] someValue = ...;
MyEntity[] items = context.MyEntities
    .Where(e => MyExtensions.Compare(e.Timestamp, someValue) <= 0)
    .ToArray();

What is the reason for this behavior? Is there any documentation about it?

  • 2

    Client side evaluation is disabled by default starting form ASP.NET Core 3.0. You can pull all your records in memory and do the comparing/processing there, or try to change your compare function to something that is actually executable in SQL

    – 

  • Mind that the intended Compare method shown in the question is an extension method but your usage is that of an ordinary static method, so it may not be considered by the compiler as you expect. Have you tried .Where(e => e.Timestamp.Compare(someValue) <= 0) ? Should give you an error that it cannot be translated to SQL, though. (given, e.Timestamp is a byte[] )

    – 




  • “but your usage is that of an ordinary static method” – at least it was before the edit.

    – 

  • I updated the question. The behavior is the same when using an extension method (as in the updated question) and without using it as an extension method. As soon as renaming the method the client side evaluation exception is thrown (as expected)

    – 

  • 1

    @Drast, we do not need your example to repeat everything again. As mentioned above, you have to use User-Defined function mapping or do comparing on the client side. When EF Core found unknown method in LINQ query – it fails.

    – 




It is how current EF Core provides translation:

If you follow this link, you will find that EF Core translates any Compare and CompareTo methods with similar signatures.

if (method.ReturnType == typeof(int))
{
    SqlExpression? left = null;
    SqlExpression? right = null;
    if (method.Name == nameof(string.Compare)
        && arguments.Count == 2
        && arguments[0].Type == arguments[1].Type)
    {
        left = arguments[0];
        right = arguments[1];
    }
    else if (method.Name == nameof(string.CompareTo)
                && arguments.Count == 1
                && instance != null
                && instance.Type == arguments[0].Type)
    {
        left = instance;
        right = arguments[0];
    }
    ...

Leave a Comment