2016-02-13 5 views
3

При использовании:C# Generic DateTime.ToString() с пользовательским форматом

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

Entity Framework производит:

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

Это используется по умолчанию дата в формате «гггг чч в пн дд: Miam (или PM)»

Я хотел бы пользователь„гггг-мм-дд чч: ми: сс (24 часа)“, который можно получить что-то вроде:

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», и не всегда в год. Пользователь может ввести время или «01», чтобы вызвать все записи либо в первый день месяца, январь, либо в 2001 году. Это очень гибкий фильтр.

Я также понимаю, что многие люди не нравится эта ситуация, но я действительно ищет решения здесь и не сказали «не делать этого»

Решение также необходимо для удовлетворения LINQ для лиц , поэтому я не могу просто. ToString («MMM d yyyy H: mm tt»), так как это приведет к:

«LINQ to Entities не распознает метод« System.String ToString (System.String) », и этот метод не может быть переведен в выражение хранилища ».

Код работает со стандартным форматом даты. Причина моего вопроса - изменить формат даты на уровне SQL, манипулируя запросом в Entity Framework.

+0

Я не думаю, что с помощью LIKE с датами такая хорошая идея. Вероятно, вы могли бы получить более эффективный запрос, не преобразовывая дату в строку. Что вы на самом деле пытаетесь достичь, т. Е. Что бы вы использовали вместо «xyz» на практике? –

+0

@MattiVirkkunen, я заменил xyz лучшим примером - 2016. Текст поступает из открытого текстового поля. Если у вас есть лучший способ работать с датами, я все для этого! Он просто должен быть гибким и точным, независимо от того, что содержит строка/фильтр. –

+0

Вы не должны смешивать код и данные. Вероятно, вы должны разобрать текстовое поле в целое число (чтобы получить год). SQL имеет функцию DATEPART, вероятно, EF переведет DateTime.Year == значение в DATEPART. –

ответ

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

Абсолютно совершенный! Я был на полпути с монстром datepart-concat мерзостью, когда я достиг исключения SQL «вложенного к уровню 10». Мне нужно подробно изучить этот код, я многому научу его. Спасибо за ваше время и усилия, я очень благодарен! –

1

Если вы пытаетесь определить, является ли дата в течение года от вашего входного значения, почему нет:

DateTime.Year == 2016 //or your variable

Может быть, больше по необходимости, чем я вижу, хотя.

+0

См. Обновленный вопрос. Я добавил текст, объясняющий, почему это не поможет. –

+0

Как вы узнаете, что прошлое - месяц, день или год? – Nicarus

+0

У вас нет, и вам не нужно. Код работает со стандартным форматом даты. Причина моего вопроса - изменить формат даты на уровне SQL, манипулируя запросом в Entity Framework. –

Смежные вопросы