具有自定义格式的C#Generic DateTime.ToString()

使用时:

DateTime.ToString().Contains("2016") 

entity framework产生:

 CAST(DateValue AS nvarchar(max)) LIKE '%2016%' 

这使用默认的日期格式“mon dd yyyy hh:miAM(或PM)”

我想用“yyyy-mm-dd hh:mi:ss(24h)”,这可以通过以下方式获得:

 CONVERT(VARCHAR(max), DateValue, 20) LIKE '%2016%' 

我需要帮助将这种格式实现到现有的generics方法。

 static Expression<Func> Expr(Expression<Func> 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 Filter(this IQueryable query, List 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(call); } return query; } 

请注意,这是一个例子 – 它并不总是“2016”,并不总是一年。 用户可以键入时间,或“01”以在月份的第1天,1月或2001年调用所有记录。这是一个非常灵活的filter。

我也明白许多人不会喜欢这种情况,但我真的在这里找一个解决方案而不是被告知“不要这样做”

解决方案还需要满足LINQ to Entities,所以我不能简单.ToString(“MMM d yyyy H:mm tt”)因为这会导致:

“LINQ to Entities无法识别方法’System.String ToString(System.String)’方法,并且此方法无法转换为商店表达式。”

该代码使用默认的日期格式。 我的问题的原因是通过操纵Entity Framework中的查询来更改SQL级别的日期格式。

我发现产生所需结果的唯一方法是使用这样的表达式手动构建它

 Expression> 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(...)相比,这是一个巨大的怪物。 但至少它有效。

这是代码。 可以使用System.Linq.Expressions构建上面的表达式,但是我太懒了,并且使用了一个简单的参数替换器。

修改部分:

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

如果您尝试确定日期是否在输入值的年内,为什么不:

DateTime.Year == 2016 //or your variable

也许你的需求比我看到的更多。