【问题标题】:Dynamic LINQ (to entities) Where with nullable DateTime column动态 LINQ(到实体)在哪里具有可为空的 DateTime 列
【发布时间】:2012-02-14 08:51:25
【问题描述】:

一段时间以来,我一直在努力解决这个问题。有一些类似的案例,但解决方案不适用于我的案例。

我有一个以字符串格式返回过滤器查询的方法。该方法具有针对不同数据类型的逻辑,设置正确的值、列名等。

string filterQuery = GetFilterQuery(params);
rows = rows.Where(filterQuery);

我的问题是我在数据库中有Nullable DateTime,而我在代码端有String 表示。

我尝试了以下查询(String 表示目前可能是错误的):

"BirthDate.ToString() = \"16.2.2012 22:00:00\""

结果“日期时间”类型的方法?无法访问

"BirthDate.Value.ToString() = \"16.2.2012 22:00:00\""

结果LINQ to Entities 无法识别方法 'System.String ToString()' 方法,并且此方法无法转换为存储表达式。 p>

"BirthDate == null ? 1=1 : (DateTime)BirthDate.ToString() = \"16.2.2012 22:00:00\""

结果'.'或“(”预期

有什么办法解决这个问题吗?

更新(添加了更多关于查询生成的源代码)

var filterQueries = query.GridFilteringOptions.filters
    // remove filters that doesn't have all the required information
    .Where(o => o.name != string.Empty && o.value != string.Empty && !string.IsNullOrEmpty(o.type))
    // remove filters that are filtering other tables than current
    .Where(o => o.table == tableName) 
    .Select(filter => filter.ResolveQuery()).ToList();

if (filterQuery.Any())
{
    var filterQuery = string.Join(" And ", filterQueries);
    rows = rows.Where(filterQuery);
}

这是一个类过滤器,方法与此上下文相关

public string ResolveQuery()
{
    if (type == "Int64")
    {
        return ResolveInteger();
    }
    else if(type == "String")
    {
        return ResolveString();
    }
    else if(type == "DateTime")
    {
        return ResolveDateTime();
    }
    else
    {
        return string.Empty;
    }
}

private string ResolveDateTime()
{
    DateTime result = new DateTime();
    if (DateTime.TryParse(this.value, out result))
    {
        return string.Format("{0}.ToString() = \"{1}\"", this.name, result.ToUniversalTime());
    }
    return string.Empty;
}

private string ResolveString()
{
    switch (@operator)
    {
        default:
            return string.Format(@"{0}.StartsWith(""{1}"")", this.name, this.value);
    }            
}

private string ResolveInteger()
{
    string tmp = this.name;
    switch (@operator)
    {
        case -1:
            return string.Empty;
        case 0:
            tmp += "<";
            break;
        case 1:
            tmp += "=";
            break;
        case 2:
            tmp += ">";
            break;
        default:
            return string.Empty;
    }
    tmp += value;
    return tmp;
}

【问题讨论】:

    标签: c# linq linq-to-entities dynamic-linq


    【解决方案1】:

    LINQ to Entities 无法识别 ToString() 方法。在将结果字符串插入查询之前,您必须对其进行评估。

    要在您的问题中创建示例,您可以这样处理:

    // "BirthDate.ToString() = \"16.2.2012 22:00:00\""
    string birthdate = BirthDate.ToString();
    string query = String.Format("{0}  = \"16.2.2012 22:00:00\"", birthdate);
    
    // "BirthDate.Value.ToString() = \"16.2.2012 22:00:00\""
    string birthdate = BirthDate.Value.ToString();
    string query = String.Format("{0}  = \"16.2.2012 22:00:00\"", birthdate);
    

    "BirthDate == null ? 1=1 : (DateTime)BirthDate.ToString() = \"16.2.2012 22:00:00\"" 可能不起作用,因为 LINQ to EF 无法识别三元运算符 (? :)

    编辑:我从您的评论中了解到BirthDate 是您表中的一列,而不是变量。在这种情况下,您可以检索所有条目,将它们转换为列表,然后使用 LINQ 对对象应用过滤器(尽管您必须相应地修改您的 filterQuery):

    string filterQuery = GetFilterQuery(params);
    var filteredRows = rows.ToList().Where(filterQuery);
    

    未经测试:也许可以使用你数据库的CONVERT函数:

    string query = "CONVERT(varchar(20), BirthDate) = \"16.2.2012 22:00:00\"";
    

    【讨论】:

    • BirthDate.ToString() - 在这种情况下 BirthDate 是什么?在我的例子中只是一个列名,所以它在代码上下文中不存在。
    • 谢谢,我也会尝试未经测试的方法
    【解决方案2】:

    我的问题是我在数据库中有 Nullable DateTime 并且我有字符串表示 代码方面。

    为什么?

    更改代码端,解析字符串表示并在 LINQ 查询中为其提供适当的对象。

    "BirthDate.ToString() = \"16.2.2012 22:00:00\""

    不好 - 如果您必须进行字符串操作,请使用 InvariantCulture。此代码很脆弱,会在任何其他国家/地区破解。

    【讨论】:

    • 日期字符串格式与此问题无关。是的,示例代码中的格式不正确。它可能会更改为 UTC 整数。我试图完成的是我将有一种方法以字符串格式返回有效的“搜索条件”,以便它可以轻松地用于所有类型的日期类型。如有必要,我可以在可为空的 DateTime 上做一个例外,但我宁愿避免它。
    • 关于 InvariantCulture 和当前脆弱的代码当然是对的。
    • 无法完成。除了 sql 上的日期时间中的“介于”之外,没有有效的搜索方法,而 LINQ 可以做的事情在这里受到限制。您的通用方法更好地考虑了 LINQ 的特殊性。
    【解决方案3】:

    我目前使用 try/catch 块来解决这个问题。这增加了一些开销和丑陋,我宁愿找到替代方案,但它确实工作得非常稳固:

    try
    {
        // for strings and other non-nullables
        records = records.Where(rule.field + ".ToString().Contains(@0)", rule.data);
    }
    catch (System.Linq.Dynamic.ParseException)
    {
        // null types
        records = records.Where(rule.field + ".Value.ToString().Contains(@0)", rule.data);
    }
    

    注意:从输入变量(rule.fieldrule.data)中接受这些字段是危险的,并且可能会让您面临(困难的)盲目 SQL 注入。可以对照值的白名单检查第一个值 (rule.field),以防止这种情况发生。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-02
      • 2021-04-16
      • 1970-01-01
      • 2014-06-22
      • 2017-09-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多