EF Core 中的 SqlFunctions.DatePart 等效项

Posted

技术标签:

【中文标题】EF Core 中的 SqlFunctions.DatePart 等效项【英文标题】:SqlFunctions.DatePart equivalent in EF Core 【发布时间】:2019-03-02 22:10:23 【问题描述】:

EF Core 中以下语句的等价物是什么?

SqlFunctions.DatePart("week", x.MyDate)

EF.Functions 似乎没有DatePart 方法。

【问题讨论】:

SqlFunctions.DatePart("week", x.MyDate) 返回什么? SqlFunctions 在 .NET Core 中不存在。 没有直接的办法,但是你可以试试 DateTime inputDate = DateTime.Parse(date.Trim()); CultureInfo cul = CultureInfo.CurrentCulture; int weekNum = cul.Calendar.GetWeekOfYear(inputDate, CalendarWeekRule.FirstDay, DayOfWeek.Monday); Week 正是 .net 的 DateTime 所没有的一件事。所有其他 DateTime“部分”属性,如 Year,都被转换为 SQL 的 DATEPART 函数。但我想这就是你问“周”的原因。 【参考方案1】:

可以通过使用 DbFunctionAttribute 包装 datepart SQL 函数来使用它。 棘手的部分是告诉 ef core 不要将 datepart 类型参数作为字符串处理。示例:

DbContextef core <= 2.1:

public int? DatePart(string datePartArg, DateTime? date) => throw new Exception();

public void OnModelCreating(DbModelBuilder modelBuilder) 
    var methodInfo = typeof(DbContext).GetRuntimeMethod(nameof(DatePart), new[]  typeof(string), typeof(DateTime) );
    modelBuilder
        .HasDbFunction(methodInfo)
        .HasTranslation(args => new SqlFunctionExpression(nameof(DatePart), typeof(int?), new[]
                
                        new SqlFragmentExpression(args.ToArray()[0].ToString()),
                        args.ToArray()[1]
                ));

DbContext ef core >= 3.1(静态 SqlFunctionExpression.Create 调用而不是 ctor):

public int? DatePart(string datePartArg, DateTime? date) => throw new Exception();

public void OnModelCreating(DbModelBuilder modelBuilder) 
    var methodInfo = typeof(DbContext).GetRuntimeMethod(nameof(DatePart), new[]  typeof(string), typeof(DateTime) );
    modelBuilder
        .HasDbFunction(methodInfo)
        .HasTranslation(args => SqlFunctionExpression.Create(nameof(DatePart), new[]
                
                        new SqlFragmentExpression(args.ToArray()[0].ToString()),
                        args.ToArray()[1]
                , typeof(int?), null));

查询:

repository.Where(x => dbContext.DatePart("week", x.CreatedAt) > 10);

更多信息:https://github.com/aspnet/EntityFrameworkCore/issues/10404

注意不要在 DbContext 的接口上调用 DbFunction 方法。调用必须直接在 DbContext 实例上发生。

编辑:对于ef core 3.1,您可以使用静态方法SqlFunctionExpression.Create 而不是ctor:

【讨论】:

无法创建SqlFragmentExpression实例,构造函数是内部O_o 已修复:github.com/aspnet/EntityFrameworkCore/pull/18208 哦,我明白了,谢谢。我想除了这个之外没有其他方法可以做到这一点,对吧?有没有人致力于添加这些功能? 新的 SqlFragmentExpression(args.ToArray()[0].ToString()) 结果为 [Microsoft.EntityFrameworkCore.Query.SqlExpressions.SqlConstantExpression]【参考方案2】:

对于 EF Core 5,它甚至有所不同,因为 SqlFunctionExpression.Create 工厂现在已过时,应该改用 SqlFunctionExpression 构造。这是更新后的代码(如果将其添加到数据库上下文中):

public partial class ApplicationDbContext : DbContext

    public int? DatePart(string datePartArg, DateTimeOffset? date) => throw new InvalidOperationException($"nameof(DatePart) cannot be called client side.");

    protected override void OnModelCreating(ModelBuilder builder)
    
        var methodInfo = typeof(DbFunctionExtensions).GetMethod(nameof(DatePart));

        var datePartMethodInfo = typeof(ApplicationDbContext) // Your DB Context
            .GetRuntimeMethod(nameof(ApplicationDbContext.DatePart), new[]  typeof(string), typeof(DateTimeOffset) );
        builder.HasDbFunction(datePartMethodInfo)
           .HasTranslation(args =>
                    new SqlFunctionExpression("DATEPART",
                        new[]
                        
                            new SqlFragmentExpression((args.ToArray()[0] as SqlConstantExpression).Value.ToString()),
                            args.ToArray()[1]
                        ,
                        true,
                        new[]  false, false ,
                        typeof(int?),
                        null
                    )
                );
    

注意:您可以根据需要切换到DateTime而不是DateTimeOffset

(该异常与 EF Core 5 无关,对于稍后处理代码或尝试调用方法客户端的任何人来说更清楚)。

【讨论】:

【参考方案3】:

对于 ef core 3.1 的解决方案略有不同,应先将参数转换为 SqlConstantExpression,然后将其 Value 传递给 SqlFragmentExpression 构造函数:

public static class DbFunctionExtensions

    public static int? DatePart(string type, DateTime? date) => throw new Exception();

    public static void ConfigureDbFunctions(this ModelBuilder modelBuilder)
    
        var mi = typeof(DbFunctionExtensions).GetMethod(nameof(DatePart));

        modelBuilder.HasDbFunction(mi, b => b.HasTranslation(e =>
        
            var ea = e.ToArray();
            var args = new[]
            
                new SqlFragmentExpression((ea[0] as SqlConstantExpression).Value.ToString()),
                ea[1]
            ;
            return SqlFunctionExpression.Create(nameof(DatePart), args, typeof(int?), null);
        ));
    

【讨论】:

嗨@Gene-R,我正在尝试使用 EF Core 3.1 让它工作。我可以拨打电话,但遇到无法翻译的错误。我在其中使用 ...“DbFunctionExtensions.DatePart("w", a.ReferenceDate) ==2" 调用上述内容。非常感谢任何帮助 不应该是DbFunctionExtensions.DatePart("week", a.ReferenceDate) 吗?我也在使用 datepart 之前检查 NULL 列

以上是关于EF Core 中的 SqlFunctions.DatePart 等效项的主要内容,如果未能解决你的问题,请参考以下文章

EF6 System.Data.Entity.Core.EntityKey 在 Entity Framework Core 中的等价物是啥?

EF Core 2.0 中的动态访问表

EF Core 中的 SqlFunctions.DatePart 等效项

Net Core Worker Windows 服务中的 EF Core DBContext

EF Core中的多对多映射如何实现?

如何跳过ef core中的某些迁移?