首先在实体框架代码中使用搜索字符串搜索 DateTime
Posted
技术标签:
【中文标题】首先在实体框架代码中使用搜索字符串搜索 DateTime【英文标题】:Searching a DateTime using a search string in Entity Framework code first 【发布时间】:2017-03-11 18:17:00 【问题描述】:我有一个 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 数据库。如何在没有AsEnumerable
的情况下为上述代码构建一个 SQL Server 友好的查询,这将产生与我当前逻辑相同的结果?
【问题讨论】:
只要去掉AsEnumerable,在IQueryable级别执行查询 @Fals 你不能,它会引发异常,因为实体框架无法将(x?.TimeUtc?.ToString() ?? "N/A").Contains(model.search)
转换为 SQL 查询。
通常,人们使用范围搜索来查询日期和时间,并将它们保留在它们的原生数据格式中。 Contains
字符串搜索将毫无用处。我可能会搜索'0'
或'/'
。
@MattJohnson 这正是我想要支持的,搜索0
或/
,如果可能的话......差不多,搜索DateTime
可能包含的任何部分,如图所示给用户。你提出了一个很好的观点,也许方法是错误的,也许我应该添加一个 DB 列来存储DateTime
的字符串表示形式,并显示给用户,然后查询它。
@Alexandru 您使用的是哪个版本的实体框架?
【参考方案1】:
我找到了解决方案。这种语法似乎有效:
using (var context = new ClientEventsContext())
var items = context.Events.Where(x =>
x.TimeUtc.HasValue && x.TimeUtc.Value.ToString().Contains(model.search)
);
【讨论】:
但您在该解决方案中使用 AsEnumerable。我认为在 EF 6.1 之后,您可以在查询中使用 ToString()。 @yosoy 对不起,当我从问题中复制并粘贴它时,我忘记将其删除,没有它就可以工作。编辑了答案。 即使这个答案目前也有一些烦人的问题。例如,如果我看到这个时间戳,3/19/2012 7:22:00 AM
,搜索适用于 2012
或 7:22
,但对于 3/19/2012
失败并且对于 7:22:00
失败......它必须使用 SQL Server DateTime 格式或其他东西...所以,也许,我需要找到一种方法来编辑我的答案,以同样的方式格式化它,或者更好的方法。
现在我似乎无法弄清楚如何获得数据库的精确查询友好格式。
你知道搜索字符串的格式和文化吗?【参考方案2】:
使用SqlFunctions 库将任何内容转换为原始查询并在 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
);
【讨论】:
StringConvert
似乎只需要 double
或 decimal
吗?
@Alexandru 每个原语都有一个重载。
据我所知不适用于DateTime?
。如果您不相信我,请自行查看:msdn.microsoft.com/en-us/library/… 有 Checksum
但我不知道如何使用它。您尝试调用的方法可能有问题。
@Alexandru 当然.. 你是对的,那么你应该使用 DateName 我的错误
@Alexandru 你是对的。您的解决方案更好,因为每个原始类型都支持ToString
,而且该解决方案不绑定到 SqlServer。 StringConvert
主要用于有限的小数格式。【参考方案3】:
以下是构建和使用 LINQ to Entities 兼容转换的方法,将日期转换为 M/d/yyyy h:mm:ss tt
格式的字符串。我将使用自定义“标记”方法并使用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();
【讨论】:
这真是漂亮的代码。我希望我能给你更多的分数。 不客气,伙计 :) 你的话比一些虚拟积分更有价值!以上是关于首先在实体框架代码中使用搜索字符串搜索 DateTime的主要内容,如果未能解决你的问题,请参考以下文章