Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SqlFunctions.DatePart equivalent in EF Core

What is the equivalent of the following statement in EF Core?

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

EF.Functions doesn't seem to have a DatePart method.

like image 719
AndreFeijo Avatar asked Sep 27 '18 04:09

AndreFeijo


People also ask

Is EF core faster than EF6?

Note that for a single row, EF Core is slower than EF6 unless using context pooling; this could be the cost of setting up all the scoped services for each instance (which isn't done when context pooling is on). For multiple rows, EF Core batches and EF6 doesn't, so EF Core quickly becomes much faster.

What is the difference between EF6 and EF core?

EF 6 is a stable and mature ORM while EF Core is relatively new. Microsoft rebuilt EF Core from the ground up and removed many of the internal dependencies and providers that EF 6 had (like SQLClient). In the long run, that will make EF Core much more extensible and lighter weight.

Does EF core support Postgres?

Npgsql Entity Framework Core provider for PostgreSQL EntityFrameworkCore. PostgreSQL is the open source EF Core provider for PostgreSQL. It allows you to interact with PostgreSQL via the most widely-used .

Does EF core support JSON?

EF Core 7.0 (EF7) adds support for mapping aggregate types to JSON documents stored in “JSON columns” of a relational database. This allows relational databases to directly store documents while retaining the overall relational structure of the data.


2 Answers

It is possible to make use of the datepart SQL function by wrapping it with the DbFunctionAttribute. Tricky part is to tell ef core not to handle the datepart type parameter as a string. Example:

DbContext ef 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 (static SqlFunctionExpression.Create call instead of 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));
}

Query:

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

some more info: https://github.com/aspnet/EntityFrameworkCore/issues/10404

watch out not to call the DbFunction method on an interface of DbContext. The call must happend directly on the DbContext instance.

Edit: for ef core 3.1 you can use the static method SqlFunctionExpression.Create instead of the ctor:

like image 124
cyptus Avatar answered Sep 24 '22 01:09

cyptus


For EF Core 5, it's even different as the SqlFunctionExpression.Create factory is now obsolete and the SqlFunctionExpression construction should be used instead. Here's the updated code (if adding it to the DB Context):

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
                    )
                );
    }
}

Note: You can switch to DateTime instead of DateTimeOffset as per your needs.

(The exception has nothing to do with EF Core 5, it's just more clear for anybody working on the code later on or trying to call the method client side).

like image 38
Ziad Akiki Avatar answered Sep 23 '22 01:09

Ziad Akiki