首先在实体框架代码中使用搜索字符串搜索日期时间
本文关键字:搜索 字符串 日期 时间 实体 框架 代码 | 更新日期: 2023-09-27 18:15:30
我有一个MVC项目,它输出一个可空的DateTime (DateTime?
)作为字符串给每个模型项目的用户,如下所示(我的用户界面有分页,所以我只对少量记录进行此计算-换句话说,这部分是可以的):
foreach (DocumentEvent item in Model.items)
@(item?.TimeUtc?.ToString() ?? "N/A")
我想添加一个搜索功能。我试着搜索如下,但这不是性能,因为AsEnumerable
物化了我的列表,我现在在c#世界,枚举每条记录:
using (var context = new ClientEventsContext())
var items = context.Events.AsEnumerable().Where(x => {
(x?.TimeUtc?.ToString() ?? "N/A").Contains(model.search)
});
相反,我想利用我的SQL Server数据库。我如何建立一个SQL服务器友好的查询上述代码没有AsEnumerable
,将产生相同的结果作为我当前的逻辑?
这里是如何构建和使用LINQ到实体兼容转换从日期到M/d/yyyy h:mm:ss tt
格式的字符串。我将使用自定义"marker"方法,并使用ExpressionVisitor
绑定实现,而不是将怪物嵌入查询中。这样,如果需要的话,您可以尝试并更改格式(甚至添加一些控制参数),而不会影响查询的可读性。
首先,实现:
public static class EFExtensions
{
public static string ToCustomDateFormat(this DateTime value)
{
// Should never happen
throw new InvalidOperationException();
}
public static IQueryable<T> ApplyCustomDateFormat<T>(this IQueryable<T> source)
{
var expression = new CustomDateFormatBinder().Visit(source.Expression);
if (source.Expression == expression) return source;
return source.Provider.CreateQuery<T>(expression);
}
class CustomDateFormatBinder : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.DeclaringType == typeof(EFExtensions) && node.Method.Name == "ToCustomDateFormat")
{
var date = Visit(node.Arguments[0]);
var year = DatePart(date, v => DbFunctions.Right("0000" + v.Year, 4));
var month = DatePart(date, v => v.Month.ToString());
var day = DatePart(date, v => v.Day.ToString());
var hour = DatePart(date, v => (1 + (v.Hour + 11) % 12).ToString());
var minute = DatePart(date, v => DbFunctions.Right("0" + v.Minute, 2));
var second = DatePart(date, v => DbFunctions.Right("0" + v.Second, 2));
var amPM = DatePart(date, v => v.Hour < 12 ? "AM" : "PM");
var dateSeparator = Expression.Constant("/");
var timeSeparator = Expression.Constant(":");
var space = Expression.Constant(" ");
var result = Expression.Call(
typeof(string).GetMethod("Concat", new Type[] { typeof(string[]) }),
Expression.NewArrayInit(typeof(string),
month, dateSeparator, day, dateSeparator, year, space,
hour, timeSeparator, minute, timeSeparator, second, space, amPM));
return result;
}
return base.VisitMethodCall(node);
}
Expression DatePart(Expression date, Expression<Func<DateTime, string>> part)
{
var parameter = part.Parameters[0];
parameterMap.Add(parameter, date);
var body = Visit(part.Body);
parameterMap.Remove(parameter);
return body;
}
Dictionary<ParameterExpression, Expression> parameterMap = new Dictionary<ParameterExpression, Expression>();
protected override Expression VisitParameter(ParameterExpression node)
{
Expression replacement;
return parameterMap.TryGetValue(node, out replacement) ? replacement : node;
}
}
}
则用法:
var items = context.Events
.Where(x => x.TimeUtc != null &&
x.TimeUtc.Value.ToCustomDateFormat().Contains(model.search))
.ApplyCustomDateFormat();
我找到了一个解决方案。这个语法似乎可以工作:
using (var context = new ClientEventsContext())
var items = context.Events.Where(x => {
x.TimeUtc.HasValue && x.TimeUtc.Value.ToString().Contains(model.search)
});
另一个使用SqlFunctions库将任何内容转换为Raw Query并在SQL级别完成工作的解决方案,例如:
using (var context = new ClientEventsContext())
var items = context.Events.Where(x =>
SqlFunctions.PatIndex(model.search,
SqlFunctions.DateName('your pattern here', x?.TimeUtc) ?? "N/A").Value > -1
);