C#Generic DateTime.ToString()与自定义格式 [英] C# Generic DateTime.ToString() with custom format

查看:154
本文介绍了C#Generic DateTime.ToString()与自定义格式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用时:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆