根据上下文将 DbFunctions 切换到非数据库实现

Switch DbFunctions to a non-db implementation depending on context

我有一个表达式使用 DbFunctions class 来计算日期。它适用于 linq-to-entities 但我想将此表达式重新用于非数据库功能。是否可以检测是否在数据库上下文中评估表达式并使用相应的实现?我已经看到涉及模拟对象的单元测试解决方案,但我需要在通用业务逻辑中使用此表达式,因此模拟看起来不是一个好的解决方案。

这是我的表达:

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

我希望能够做这样的事情:

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

其中 MyCustomResolver 将根据上下文使用 DbFunctionsDateTime 实现。

interface ICustomResolver
{
    DateTime AddMinutes(DateTime, int);
}

public class DbCustomResolver : ICustomResolver
{
    public DateTime AddMinutes(DateTime time, int minutes)
    {
        return DbFunctions.AddMinutes(time, minutes);
    }
}

public class NonDbCustomResolver : ICustomResolver
{
    public DateTime AddMinutes(DateTime time, int minutes)
    {
        return time.AddMinutes(minutes);
    }
}


ICustomResolver MyCustomResolver = 
   amIUsingDB ? new DbCustomResolver() : new NonDbCustomResolver();

现在,我想人们可以在 AddMinutes 中即时做出决定,但为什么呢? if() 最好只做一次,而不是每次都做。

最后我采用了以下方法:

  1. 定义一个带有 DbFunction 属性的函数。这样,如果在 Linq-to-Entities 表达式中使用,它会使用相应的 SQL 函数;如果在非数据库上下文中执行,它会使用 C# 函数

    /// <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. 使用上面定义的辅助函数构建表达式

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