【问题标题】:Queryable WHERE Contains String, Int, DateTime可查询的 WHERE 包含 String、Int、DateTime
【发布时间】:2017-07-22 12:39:05
【问题描述】:

我正在处理这行代码:

query = query.Where(p => 
  p.ChckNumber.ToString().Contains(globalSearch.ToString()) || 
  p.BankAccount.ToString().Contains(globalSearch.ToString()) || 
  p.Description.ToString().Contains(globalSearch.ToString()) || 
  p.CheckAmount.ToString().Contains(globalSearch) || 
  p.ClearedDate.ToString().Contains(globalSearch.ToString()) || 
  p.SentDate.ToString().Contains(globalSearch.ToString()));

使用这行代码我基本上是在进行搜索,当globalSearchstring 并且BankAccountDescription 之类的列是varchar 时,它似乎工作正常,但是,当@987654327 @ 是 IntDateTime(-2233 或 4/9/2013)列 CheckAmount (int)ClearedDate (DateTime)SentDate (DateTime),它返回 0 行,如果 globalSearch 匹配 intChckNumber 有效!

我做错了什么?

我在 SQL Server 中运行了这个查询:

SELECT CONVERT(VARCHAR(MAX), ClearedDate) FROM myTable

我所有的日期都返回 2017 年 9 月 9 日

【问题讨论】:

  • 因此,如果用户键入“2”,您希望返回任何数字或日期字段包含 2 的所有项目(如数字 112 或日期 01/03/2017)。似乎不是很有用。
  • 请在 sql profiler 的帮助下检查触发了什么查询。这将使您了解如何调整 linq。我猜它与 ToString() 如何将数据转换为字符串以及采用哪种格式有关。
  • 尝试逐步调试您的代码,1-尝试检查“Tostring”方法是否在您的输入数据上正常工作。 2-尝试检查您的 sql-server 数据库中的日期时间格式是否与您的日期时间输入一致
  • 您是否尝试过使用ToString 中的格式?例如:p.ClearedDate.ToString("MM/dd/yyyy")
  • 你对日期的逻辑是什么?你想要精确的日期匹配吗?

标签: c# sql iqueryable


【解决方案1】:

抱歉回复晚了

不要将 ToString 用于数字

改为检查 globalsearch 字符串是否为 NUMeric,

如果它是数字,则将其转换为整数或双精度等必填字段。 至于日期时间,

使用日期格式化程序作为 p.ClearedDate.ToString("dd/MM/yyyy") 并且不需要格式化全局搜索,因为它将采用给定的格式

我的解决方案可能是这样的,

查询 = 查询.Where(p => p.ChckNumber==int.TryParse(globalSearch.ToString, out num)?num:globalSearch.ToString(); || p.BankAccount.ToString().Contains(globalSearch.ToString()) || p.Description.ToString().Contains(globalSearch.ToString()) || p.CheckAmount==decimal.TryParse(globalSearch.ToString, out num)?num:globalSearch.ToString(); || p.ClearedDate.ToString("dd/MM/yyyy").Contains(globalSearch.ToString()) || p.SentDate.ToString("dd/MM/yyyy").Contains(globalSearch.ToString()));

【讨论】:

    【解决方案2】:

    您的代码将返回查询中第一个字段与查询词匹配的行。这是现实的要求吗?我建议不要。查询词必须针对已知的特定字段。

    您是否真的有兴趣将查询词作为字段内容的子字符串查找?至少在整数和日期字段的情况下,这似乎不太可能,但由于您没有说明查询词可以包含的值,因此无法确定。

    没有必要转换 varchar 字段.ToString(),因为它们已经是字符串。同样,如果您在查询DateTimeInteger 字段时提供适当类型的查询词,您也不需要转换它们(假设您实际上不是在查询子字符串)。这将避免空字段值的问题,并消除更多的冗余处理。

    您可能希望根据这些要点重新设计您的查询。我建议您采用更深思熟虑的方法,而不是使用“一刀切”的查询对象。

    例如,您可以为每个查询字段创建一个包含可为空属性的查询对象。

    public partial class AccountQuery
    {
        public string CheckNumber { get; set; }
        public string BankAccount { get; set; }
        public string Description { get; set; }
        public int? CheckAmount { get; set; }
        public DateTime? ClearedDate { get; set; }
        public DateTime? SentDate { get; set; }
    }
    

    在您的实体模型/DTO 中添加一个方法来封装查询逻辑。它接受一个 AccountQuery 对象并将其上的任何非空属性与相应的实体属性匹配。

    public class Account
    {
        public string CheckNumber { get; set; }
        public string BankAccount { get; set; }
        public string Description { get; set; }
        public int CheckAmount { get; set; }
        public DateTime ClearedDate { get; set; }
        public DateTime SentDate { get; set; }
        public bool Matches(AccountQuery query)
        {
            return (!string.IsNullOrEmpty(query.CheckNumber) && query.CheckNumber == CheckNumber) ||
                   (!string.IsNullOrEmpty(query.BankAccount) && query.BankAccount == BankAccount) ||
                   (!string.IsNullOrEmpty(query.Description) && query.Description == Description) ||
                   (query.ClearedDate.HasValue && query.ClearedDate == ClearedDate) ||
                   (query.SentDate.HasValue && query.SentDate == SentDate) ||
                   (query.CheckAmount.HasValue && query.CheckAmount == CheckAmount);
        }
    }
    

    然后您的查询变为,例如:

    var aq = new AccountQuery
    {
        ClearedDate = DateTime.Today
    };
    query.Where(p => p.Matches(aq));
    

    请注意,此查询的逻辑与您的原始代码不同,正如我在上面指出的那样,这似乎不现实。

    【讨论】:

      【解决方案3】:

      您应该根据您的要求使用格式。由数字编码的所有日期时间格式。

      例如,110 -> mm-dd-yyyy

       SELECT CONVERT(VARCHAR(MAX), ClearedDate, 110) FROM myTable
      

      【讨论】:

        【解决方案4】:

        尝试格式化您的日期部分

         p.ClearedDate.ToString("dd/MM/yyyy").Contains(globalSearch.ToString("dd/MM/yyyy"))
        

        还可以尝试 linq Pad 或类似工具或任何 linq 助手扩展,看看会出现什么 SQL

        【讨论】:

        • 为什么在这里使用Contains?这样的日期字符串将永远不会包含除自身之外的任何其他日期字符串,并且在这种情况下等于它。因此,平等性测试在这里会做同样的事情,并且可能会更快。
        • 同意你的观点,而不是这个我使用另一种方法,首先检查日期是否大于等于和大于嵌套 if(如果可能)检查小于条件。即使格式有一点变化(2017 年 3 月 3 日或 2017 年 3 月 3 日),这种方法总是对我有用
        【解决方案5】:

        单字符日期格式。我们可以使用 char 作为 ToString 的参数来指定预设格式。这些是标准格式。

        query = query.Where(p => 
        p.ChckNumber.ToString().Contains(globalSearch.ToString()) || 
        p.BankAccount.ToString().Contains(globalSearch.ToString()) || 
        p.Description.ToString().Contains(globalSearch.ToString()) || 
        p.CheckAmount.ToString().Contains(globalSearch) || 
        p.ClearedDate.ToString("d").Contains(globalSearch.ToString()) || 
        p.SentDate.ToString("d").Contains(globalSearch.ToString()));
        

        ToShortDateString() 等价于小写 d ToString("d")

        query = query.Where(p => 
        p.ChckNumber.ToString().Contains(globalSearch.ToString()) || 
        p.BankAccount.ToString().Contains(globalSearch.ToString()) || 
        p.Description.ToString().Contains(globalSearch.ToString()) || 
        p.CheckAmount.ToString().Contains(globalSearch) || 
        p.ClearedDate.ToShortDateString().Contains(globalSearch.ToString()) || 
        p.SentDate.ToShortDateString().Contains(globalSearch.ToString()));
        

        【讨论】:

          【解决方案6】:

          您好,如果您正在寻找正确的日期格式,请试试这个。

          SELECT CONVERT(VARCHAR(10), SYSDATETIME(), 103)
          

          【讨论】:

            【解决方案7】:

            您不应该关心 globalSearch 是否为 Int,因为您应该始终将其转换为字符串,除非 DateTime 并且您不应该关心列是 varchar 还是 Int,因为您始终将列转换为字符串,除非 DateTime .

            请注意,我根据您的 cmets 做出假设,即您只比较月、日和年,而不关心 ClearedDate 的时间

            我发现您的代码存在三个潜在问题:

            1. 您没有将 globalSearch 转换为 CheckAmount 列的字符串
            2. 在调用 .ToString 之前,您不会检查任何列的空值,也许您的所有列都不允许空值
            3. 您的代码可读性不强,人眼很难发现不一致之处。

            这里是更易读的代码,它每次都将 globalSearch 转换为字符串,因为您不会将 globalSearch 转换为 CheckAmount 的字符串:

            var gsStr = globalSearch.ToString();
            var gsDate = DateTime.MinDate;
            if(globalSearch.GetType() == typeof(DateTime))
            {
                gsDate = globalSearch;
            }
            query = query.Where(p => p.ChckNumber.ToString().Contains(gsStr) 
            || p.BankAccount.ToString().Contains(gsStr) 
            || p.Description.ToString().Contains(gsStr) 
            || p.CheckAmount.ToString().Contains(gsStr) 
            || p.ClearedDate.Date == gsDate.Date
            || p.SentDate.ToString().Contains(gsStr));
            

            如果您使用的是 C# 6.0,则可以使用新的 Null Propagation Operator 来防止您的任何列现在或将来允许空值时出现错误:

            var gsStr = globalSearch.ToString();
            var gsDate = DateTime.MinDate;
            if(globalSearch.GetType() == typeof(DateTime))
            {
                gsDate = globalSearch;
            }
            query = query.Where(p => p.ChckNumber?.ToString().Contains(gsStr) 
            || p.BankAccount?.ToString().Contains(gsStr) 
            || p.Description?.ToString().Contains(gsStr) 
            || p.CheckAmount?.ToString().Contains(gsStr) 
            || p.ClearedDate?.Date == gsDate.Date
            || p.SentDate?.ToString().Contains(gsStr));
            

            将 GlobalSearch ToString 转换为 CheckAmount Contains 可能会解决您的问题,使您的代码更具可读性并防止出现空值,如果它没有修复错误,则更容易调试。

            【讨论】:

            • 这个问题有 sql 标签,所以可能使用了实体框架,如果是这样,带有 .ToString() 的代码很可能不会在那里工作。
            • 我在 SQL Server SELECT CONVERT(VARCHAR(MAX), ClearedDate) FROM myTable 中运行了这个查询,我的所有日​​期都返回 2017 年 9 月 9 日 如果 globalSearch 是 2017 年 9 月 10 日,它可以工作,但它不适用于我需要的格式 2017 年 9 月 9 日
            • @user979331 我更新了我的答案以与 DateTime.Dates 进行比较
            • @user979331 答案不错,试试看。另外,当您将 CONVERT 用于日期时,您应该将所需格式添加为参数,请看这里w3schools.com/sql/func_convert.asp
            猜你喜欢
            • 1970-01-01
            • 2015-10-30
            • 2012-07-11
            • 1970-01-01
            • 1970-01-01
            • 2016-12-13
            • 2020-05-09
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多