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