Динамическое LINQ (для сущностей) Где с обнуляемым столбцом DateTime - PullRequest
5 голосов
/ 14 февраля 2012

Я уже давно бьюсь над этой проблемой.Есть несколько подобных случаев, но решения не были применимы в моем случае.

У меня есть метод, который возвращает запрос фильтра в строковом формате.Метод имеет логику для разных типов данных, устанавливает правильные значения, имена столбцов и т. Д.

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

Моя проблема в том, что у меня есть Nullable DateTime в базе данных и у меня есть String представление на стороне кода.

Я пробовал следующие запросы (String представление может быть неправильным в настоящее время):

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

Результат : Методы для типа 'DateTime?'недоступны

"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 ]

2 голосов
/ 14 февраля 2012

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\"";
0 голосов
/ 10 июля 2014

Я сейчас решаю это с помощью блока 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.field и rule.data) опасно и может привести к (сложному) слепому внедрению SQL. Первое значение (rule.field) можно проверить по белому списку значений, чтобы предотвратить это.

0 голосов
/ 14 февраля 2012

Моя проблема в том, что у меня есть Nullable DateTime в базе данных, и у меня есть строковое представление в сторона кода.

Почему?

Изменить сторону кода, разобрать строковое представление и дать ему правильный объект в запросе LINQ.

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

ПЛОХО - Если вам нужно манипулировать строками, AWLWAYS использует InvariantCulture. Этот код хрупок и сломается в любой другой стране.

...