Search code examples
c#linqlinq-to-entitiesexpression

Switch DbFunctions to a non-db implementation depending on context


I have an expression that uses DbFunctions class to calculate dates. It works fine for linq-to-entities but I want to reuse this expression for non-db functionality. Is it possible to detect if expression is evaluated in db context or not and use corresponding implementation? I've seen solution for unit tests which involves mock objects but I need this expression in common business-logic so mocks don't look like a good solution.

Here is my expression:

public static Expression<Func<Transaction, bool>> Expired(int expirationPeriod)
{
    return s => s.Status == Status.Created && DbFunctions.AddMinutes(s.UpdateDateUTC, expirationPeriod) < DateTime.UtcNow;
}

I want to be able to do something like this:

public static Expression<Func<Transaction, bool>> Expired(int expirationPeriod)
{
    return s => s.Status == Status.Created && MyCustomResolver.AddMinutes(s.UpdateDateUTC, expirationPeriod) < DateTime.UtcNow;
}

where MyCustomResolver will use either DbFunctions or DateTime implementation depending on context.


Solution

  • Finally I used the following approach:

    1. Define a function with DbFunction attribute. This way it uses corresponding SQL function if used in Linq-to-Entities expression or C# function if executed in non-db context

      /// <summary>
      /// Utilize functions for both Linq-to-Entities and non-db context
      /// </summary>
      public static class MyCustomResolver
      {
          [DbFunction("Edm", "AddMinutes")]
          public static DateTime? AddMinutes(DateTime? timeValue, int addValue)
          {
              return timeValue?.AddMinutes(addValue);
          }
      }
      
    2. Build expressions using helper function defined above

      public static Expression<Func<Transaction, bool>> Expired(int expirationPeriod)
      {
          return s => s.Status == Status.Created && MyCustomResolver.AddMinutes(s.UpdateDateUTC, expirationPeriod) < DateTime.UtcNow;
      }