3

使用時:

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%'

この形式を既存のジェネリック メソッドに実装するのに助けが必要です。

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」であるとは限らず、常に年であるとは限らないことに注意してください。ユーザーは、月の最初の日、1 月、または 2001 年のいずれかの時刻または「01」を入力して、すべてのレコードを呼び出すことができます。これは非常に柔軟なフィルターです。

多くの人がこの状況を好まないことも理解していますが、私はここで解決策を本当に探しており、「これをしないでください」と言われません。

このソリューションは、LINQ to Entities にも対応する必要があるため、単純に .ToString("MMM d yyyy H:mm tt") を実行することはできません。

「LINQ to Entities はメソッド 'System.String ToString(System.String)' メソッドを認識しないため、このメソッドはストア式に変換できません。」

コードはデフォルトの日付形式で動作します。私の質問の理由は、Entity Framework でクエリを操作して、SQL レベルで日付形式を変更することです。

4

2 に答える 2

1

望ましい結果を生成するために私が見つけた唯一の方法は、このような式で手動で構築することです

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(...)。しかし、少なくともそれは機能します。

これがコードです。を使用して上記の式を作成することもでき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<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);
    }
}
于 2016-02-14T00:36:39.830 に答える