Обходной путь для использования метода в выражении LINQ - PullRequest
3 голосов
/ 21 февраля 2012

В моем приложении ASP.NET MVC3 у меня есть следующий метод, который использует EF-сущности :

public IQueryable<Products> GetCreatedProducts(int year)
{
        return GetAllProducts().Where(m => Equals(DateUtilities.ExtractYear(m.ProductCreationDate), year));
}

ProductCreationDate - это поле, хранящееся в базе данных в виде строки "ГГГГММддччмм" .

int DateUtilities.ExtractYear(string date) - это метод, который я создал, чтобы получить год из строки.Точно так же я использую ExtractMonth и ExtractDay.

Однако, когда я выполняю свое приложение, оно запускает исключение NotSupported :

"LINQ to Entities нераспознайте метод метода Int32 ExtractYear (System.String), и этот метод не может быть преобразован в выражение хранилища. "

Путем поиска проблемы в Google я обнаружил, что это ошибка в LINQ to Entities Кто-нибудь знает обходной путь?

Ответы [ 5 ]

4 голосов
/ 21 февраля 2012

В этом случае вы можете пойти другим путем:

GetAllProducts().Where(m => m.ProductCreationDate.StartsWith(year.ToString()));

Вместо того, чтобы извлекать год из строки, найдите все строки, начинающиеся с года, после которого вы.

IЯ не уверен, что они будут работать для поиска по месяцу и дню:

var monthString = String.Format("{0:00}", month);
GetAllProducts().Where(m => m.ProductCreationDate.Substring(4,2) == monthString);  

var dayString = String.Format("{0:00}", day);
GetAllProducts().Where(m => m.ProductCreationDate.Substring(6,2) == dayString);
4 голосов
/ 21 февраля 2012

Мой первый вопрос: почему вы храните дату в базе данных в виде строки?Сохраните его как дату и используйте сравнение данных с ним через Linq.

Проблема, с которой вы сталкиваетесь, заключается в том, что Linq не понимает, каков метод ExtractYear, когда он пытается скомпилировать ваш оператор Linq в rawSQL

РЕДАКТИРОВАНИЕ Другой альтернативой может быть создание представления в базе данных и запрос к представлению с вычисляемым столбцом, который представляет строковое значение в виде даты и времени.

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

Я не думаю, что это действительно ошибка, а скорее ограничение - нет версии SQL ExtractYear, поэтому выполнить этот запрос невозможно.

Вам нужно будет переписать запрос, чтобы использовать только SQL-совместимые команды (как показали некоторые другие ответы) или извлечь много данных из базы данных и выполнить фильтрацию с LINQ-to-Objects (это может потенциально быть очень дорогим, в зависимости от того, что вы запрашиваете и сколько записей совпадают).

1 голос
/ 21 февраля 2012

Вы получаете исключение, потому что вы работаете с IQueryable.Entity Framework попытается перевести предикат в предложении where в SQL, но EF не знает, как перевести ваш метод.

Если вы не хотите (или не можете) изменить базу данных, вы все ещеесть несколько вариантов:

  1. Потяните все строки на стороне клиента и выполните фильтрацию на клиенте.Вы можете сделать это, изменив значение с IQueryable на IEnumerable:

    return GetAllProducts()
      .AsEnumerable()
      .Where(m => Equals(DateUtilities.ExtractYear(m.ProductCreationDate), year))
      .AsQueryable();
    
  2. Чтобы повысить эффективность, вы можете использовать собственный SQL (для этого требуется EF 4.1 и только избегать извлечения всех продуктов).клиенту, а не сканированию на сервере):

    return context
      .Products
      .SqlQuery("select * from Products where substring(ProductCreationDate, 1, 4) = '2012'")
      .AsQueryable();
    

    Обратите внимание, что я был ленив и жестко запрограммировал SQL.Вам, вероятно, следует параметризовать его и убедиться, что вы избегаете атак SQL-инъекций.

1 голос
/ 21 февраля 2012

Вы можете сделать извлечение встроенным, что-то вроде этого:

public IQueryable<Products> GetCreatedProducts(int year)
{
        string sYear = year.ToString();
        return GetAllProducts().Where(m => m.ProductCreationDate.Substring(0,4) == sYear);
}
...