C#Generic DateTime.ToString()与自定义格式 [英] C# Generic DateTime.ToString() with custom format
问题描述
使用时:
DateTime.ToString().Contains("2016")
实体框架产生:
CAST(DateValue AS nvarchar(max)) LIKE '%2016%'
这使用默认的日期格式mon dd yyyy hh:miAM(或PM)
This uses the default date-format "mon dd yyyy hh:miAM (or PM)"
我想使用yyyy-mm-dd hh:mi:ss(24h)如下所示:
I would like to user "yyyy-mm-dd hh:mi:ss (24h)" which is obtainable with something like:
CONVERT(VARCHAR(max), DateValue, 20) LIKE '%2016%'
我需要帮助实现这种格式到现有的通用方法。
I need help implementing this format to an existing generic method.
static Expression<Func<T, TResult>> Expr<T, TResult>(Expression<Func<T, TResult>> source) { return source; }
static MethodInfo GetMethod(this LambdaExpression source) { return ((MethodCallExpression)source.Body).Method; }
static readonly MethodInfo Object_ToString = Expr((object x) => x.ToString()).GetMethod();
static readonly MethodInfo String_Contains = Expr((string x) => x.Contains("y")).GetMethod();
public static IQueryable<T> Filter<T>(this IQueryable<T> query, List<SearchFilterDto> filters)
where T : BaseEntity
{
if (filters != null && filters.Count > 0 && !filters.Any(f => string.IsNullOrEmpty(f.Filter)))
{
var item = Expression.Parameter(query.ElementType, "item");
var body = filters.Select(f =>
{
var value = f.Column.Split('.').Aggregate((Expression)item, Expression.PropertyOrField);
if (value.Type != typeof(string))
{
value = Expression.Call(value, Object_ToString);
}
return (Expression)Expression.Call(value, String_Contains, Expression.Constant(f.Filter));
})
.Where(r => r != null)
.Aggregate(Expression.AndAlso);
var predicate = Expression.Lambda(body, item);
MethodInfo whereCall = (typeof(Queryable).GetMethods().First(mi => mi.Name == "Where" && mi.GetParameters().Length == 2).MakeGenericMethod(query.ElementType));
MethodCallExpression call = Expression.Call(whereCall, new Expression[] { query.Expression, predicate });
query = query.Provider.CreateQuery<T>(call);
}
return query;
}
请注意,这是一个例子 - 它并不总是2016并不总是一年。用户可以键入时间,或者01可以在每月的第一天,一月或二零零一年的时候回忆所有记录。这是一个非常灵活的过滤器。
Please note, this is an example - it will not always be "2016" and not always be a year. The user may type the time, or "01" to recall all records either on the 1st day of the month, January or in 2001. It's a very flexible filter.
我也明白许多人不会喜欢这种情况,但我真的在寻找一个解决方案,而不是被告知不要这样做
I also understand that many people will not like this situation, but I am really looking for a solution here and not be told "don't do this"
解决方案也需要适应LINQ to Entities,所以我不能简单地.ToString(MMM d yyyy H:mm tt),因为这将导致:
The solution also needs to cater for LINQ to Entities, so I cant simply .ToString("MMM d yyyy H:mm tt") as this will result in:
LINQ to Entities不能识别方法'System.String ToString(System.String)'方法,并且此方法不能转换为存储表达式。
"LINQ to Entities does not recognize the method 'System.String ToString(System.String)' method, and this method cannot be translated into a store expression."
代码与默认日期格式。我的问题的原因是通过操作Entity Framework中的查询来更改SQL级别的日期格式。
The code works with the default date-format. The reason for my question is to change the date-format at SQL level, by manipulating the query in Entity Framework.
推荐答案
我发现产生所需结果的唯一方法是用这样的表达式手动构建它。
The only way I found to produce the desired result is manually building it with expression like this
Expression<Func<DateTime, string>> Date_ToString = date =>
DbFunctions.Right("000" + date.Year.ToString(), 4) + "-" +
DbFunctions.Right("0" + date.Month.ToString(), 2) + "-" +
DbFunctions.Right("0" + date.Day.ToString(), 2) + " " +
DbFunctions.Right("0" + date.Hour.ToString(), 2) + ":" +
DbFunctions.Right("0" + date.Minute.ToString(), 2) + ":" +
DbFunctions.Right("0" + date.Second.ToString(), 2);
我知道。坦白说,你不希望看到EF生成的SQL从上面的表达式 - 一个巨大的怪物相比,所需的 CONVERT(...)
。但至少它有效。
Ugly, I know. And frankly you don't want to see the EF generated SQL from the above expression - a huge monster compared to the desired CONVERT(...)
. But at least it works.
这是代码。可以使用 System.Linq.Expressions
构建上述表达式,但是我太懒了,并且使用了一个简单的参数替换器。
Here is the code. One could build the above expression using System.Linq.Expressions
, but I'm too lazy for that and used a simple parameter replacer.
修改后的部分:
if (value.Type != typeof(string))
{
if (value.Type == typeof(DateTime))
value = value.ToDateString();
else if (value.Type == typeof(DateTime?))
value = Expression.Condition(
Expression.NotEqual(value, Expression.Constant(null, typeof(DateTime?))),
Expression.Property(value, "Value").ToDateString(),
Expression.Constant(""));
else
value = Expression.Call(value, Object_ToString);
}
和使用过的助手:
static readonly Expression<Func<DateTime, string>> Date_ToString = date =>
DbFunctions.Right("000" + date.Year.ToString(), 4) + "-" +
DbFunctions.Right("0" + date.Month.ToString(), 2) + "-" +
DbFunctions.Right("0" + date.Day.ToString(), 2) + " " +
DbFunctions.Right("0" + date.Hour.ToString(), 2) + ":" +
DbFunctions.Right("0" + date.Minute.ToString(), 2) + ":" +
DbFunctions.Right("0" + date.Second.ToString(), 2);
static Expression ToDateString(this Expression source)
{
return Date_ToString.ReplaceParameter(source);
}
static Expression ReplaceParameter(this LambdaExpression expression, Expression target)
{
return new ParameterReplacer { Source = expression.Parameters[0], Target = target }.Visit(expression.Body);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
这篇关于C#Generic DateTime.ToString()与自定义格式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!