有没有办法在 Npgsql EF Core 上检索时区后比较 LocalDate?

问题描述 投票:0回答:1

Nodatime 插件不支持参数化时区。我想创建一个自定义帮助器方法来执行下面代码的逻辑。我的 LINQ 无法翻译是因为参数化时区,还是无法编写通用表达式来比较自定义类型的值?

//Wrong
var potatoesCreatedToday = await dbContext.Potatoes.Where(m => m.CreatedOn.InZone(timezone).Date == localDate).ToListAsync;

//Correct
var potatoesCreatedToday = await dbContext.Potatoes.Where(m => m.CreatedOn.InZone(DateTimeZoneProviders.Tzdb[timezoneName]).Date == localDate).ToListAsync;

//Custom helper method
public static IQueryable<T1> InstantFilter(IQueryable<T1> source, string op, string[] values, string propertyName, DateTimeZone timezone)
{
    // Check if the type T has a property named "CreatedOn"
    var createdOnProperty = typeof(T1).GetProperty(propertyName);

    var value = Instant.FromUnixTimeMilliseconds(Convert.ToInt64(values[0].Trim().ToLower())).InZone(timezone).Date;

    if (createdOnProperty != null)
    {
        var parameter = Expression.Parameter(typeof(T1), "m");
        var propertyExpression = Expression.Property(parameter, createdOnProperty);
        var dateConstant = Expression.Constant(value, typeof(LocalDate));
        var timezoneConstant = Expression.Constant(timezone, typeof(DateTimeZone));
        MethodInfo methodInfo = typeof(Instant).GetMethod("InZone", new[] { typeof(DateTimeZone) });
        MethodCallExpression callExpression = Expression.Call(propertyExpression, methodInfo, timezoneConstant);
        var dateProperty = typeof(ZonedDateTime).GetProperty("Date");
        var propertyExpression2 = Expression.Property(callExpression, dateProperty);
        Expression<Func<T1, bool>> predicate;


        if (op == ">")
        {
            predicate = Expression.Lambda<Func<T1, bool>>(Expression.GreaterThan(propertyExpression, dateConstant), parameter);
        }
        else if (op == "<")
        {
            predicate = Expression.Lambda<Func<T1, bool>>(Expression.LessThan(propertyExpression2, dateConstant), parameter);
        }
        else if (op == "<=")
        {
            predicate = Expression.Lambda<Func<T1, bool>>(Expression.LessThanOrEqual(propertyExpression, dateConstant), parameter);
        }
        else if (op == ">=")
        {
            predicate = Expression.Lambda<Func<T1, bool>>(Expression.GreaterThanOrEqual(propertyExpression, dateConstant), parameter);
        }
        else
        {
            predicate = Expression.Lambda<Func<T1, bool>>(Expression.Equal(propertyExpression, dateConstant), parameter);
        }

        return source.Where(predicate);
    }

    return source;
}

错误信息

 .Where(t => t.CreatedOn.InZone((DateTimeZone)Asia/Singapore).Date < Friday, 8 December, 2023)' could not be translated

我尝试查找有关 npgsql 和 nodatime 的指南,但找不到解决方案,所以如果有人能指出我正确的路径,我将非常感激。

https://www.npgsql.org/efcore/mapping/nodatime.html?tabs=with-datasource#operation-translation

**更新(2023-12-12):Nodatime 插件版本 8 及更高版本现在支持参数化时区。现在出现了一个不同的问题,我认为表达式存在限制,因为 Expression.GreaterThan 可能被设计为仅支持数字值。生成的 SQL 查询缺少时区值“亚洲/新加坡”的单引号,我想不出添加它的方法。以下是生成的查询。

SELECT * FROM potatoes AS p
WHERE CAST(p.created_on AT TIME ZONE Asia/Singapore::text AS date) < DATE '2023-12-12'
c# npgsql nodatime
1个回答
0
投票

我采取了稍微不同的方法来解决这个问题。我检索了两个即时值,一个代表所选日期的开始,一个代表第二天的开始(代表所选日期的结束)。使用这两个即时值,我能够创建通用过滤器来根据给定时区比较日期。

public static IQueryable<T1> InstantDateFilter(IQueryable<T1> source, string op, string[] values, string propertyName, DateTimeZone timezone)
{
    var sourceProperty = typeof(T1).GetProperty(propertyName);

    var startOfSelectedDayValue = Instant.FromUnixTimeMilliseconds(Convert.ToInt64(values[0].Trim().ToLower()))
        .InZone(timezone)
        .Date
        .AtStartOfDayInZone(timezone)
        .ToInstant();

    var startOfNextDayValue = Instant.FromUnixTimeMilliseconds(Convert.ToInt64(values[0].Trim().ToLower()))
        .InZone(timezone)
        .Date
        .PlusDays(1)
        .AtStartOfDayInZone(timezone)
        .ToInstant();

    if (sourceProperty != null)
    {
        var parameter = Expression.Parameter(typeof(T1), "m");
        var memberExpression = Expression.Property(parameter, sourceProperty);

        if (sourceProperty.PropertyType.IsGenericType && sourceProperty.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            memberExpression = Expression.Property(memberExpression, "Value");
        }
        var startOfDayConstant = Expression.Constant(startOfSelectedDayValue, typeof(Instant));
        var startOfNextDayConstant = Expression.Constant(startOfNextDayValue, typeof(Instant));

        Expression<Func<T1, bool>> predicate;


        if (op == ">")
        {
            predicate = Expression.Lambda<Func<T1, bool>>(Expression.GreaterThanOrEqual(memberExpression, startOfNextDayConstant), parameter);
        }
        else if (op == "<")
        {
            predicate = Expression.Lambda<Func<T1, bool>>(Expression.LessThan(memberExpression, startOfDayConstant), parameter);
        }
        else if (op == "<=")
        {
            predicate = Expression.Lambda<Func<T1, bool>>(Expression.LessThan(memberExpression, startOfNextDayConstant), parameter);
        }
        else if (op == ">=")
        {
            predicate = Expression.Lambda<Func<T1, bool>>(Expression.GreaterThanOrEqual(memberExpression, startOfDayConstant), parameter);
        }
        else
        {
            var firstPredicate = Expression.Lambda<Func<T1, bool>>(Expression.GreaterThanOrEqual(memberExpression, startOfDayConstant), parameter);
            var secondPredicate = Expression.Lambda<Func<T1, bool>>(Expression.LessThan(memberExpression, startOfNextDayConstant), parameter);
            predicate = Expression.Lambda<Func<T1, bool>>(Expression.AndAlso(firstPredicate.Body, new ExpressionReplacer(secondPredicate.Parameters[0], firstPredicate.Parameters[0]).Visit(secondPredicate.Body)), parameter);
        }

        return source.Where(predicate);
    }

    return source;
}
© www.soinside.com 2019 - 2024. All rights reserved.