动态LINQ(到实体)具有可为空的DateTime列

时间:2012-02-14 08:51:25

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

我一直在讨论这个问题。有一些类似的案例,但解决方案并不适用于我的案例。

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

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()'方法,此方法无法转换为商店表达式。

"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);
}

这是一个类Filter,方法与此上下文相关

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;
}

3 个答案:

答案 0 :(得分:2)

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\"";

答案 1 :(得分:0)

  

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

为什么?

更改代码端,解析字符串表示并在LINQ查询中为其指定正确的对象。

  

“BirthDate.ToString()= \”16.2.2012 22:00:00 \“”

坏 - 如果你必须做字符串操作,AWLWAYS使用InvariantCulture。这段代码很脆弱,在任何其他国家都会破裂。

答案 2 :(得分:0)

我目前用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),以防止这种情况发生。