Как я могу использовать внешние выражения в Linq с EF4 (и LINQKit)?

Я хочу выделить часто используемые выражения в запросах linq. Я использую Entity Framework 4, а также LINQKit, но я до сих пор не знаю, как мне это сделать правильно. Позвольте мне привести Вам пример:

Article article = dataContainer.Articles.FirstOrDefault(a => a.Id == id);

IEnumerable<Comment> comments =
  (from c in article.Comments
   where CommentExpressions.IsApproved.Invoke(c)
   select c);


public static class CommentExpressions
{
    public static Expression<Func<Comment, bool>> IsApproved
    {
        get
        {
            return c => c.IsApproved;
        }
    }
}

Конечно, выражение IsApproved было бы намного сложнее.

Проблема в том, что Invoke() не будет работать, потому что я не вызывал .asExpandable() из LINQKit для container.Comments, но я не могу его вызвать, потому что это просто ICollection вместо ObjectSet.

Итак, мой вопрос: всегда ли мне нужно просматривать контекст данных, когда я хочу включить внешние выражения, или я могу каким-то образом использовать его для извлеченного объекта (статья)?

Любые идеи или лучшие практики? Большое спасибо! нео


person letmaik    schedule 30.03.2010    source источник
comment
Вы имели в виду article.Comments вместо container.Comments в строке 4 вашего примера кода?   -  person jeroenh    schedule 30.03.2010
comment
ага, спасибо, поправил   -  person letmaik    schedule 30.03.2010


Ответы (3)


Проблема в том, что EF не поддерживает выражения Invoke, поэтому вам нужно свернуть выражение в EF другим способом.

Вам следует ознакомиться с 'Дэмиена. Свойства на стороне клиента», который в основном делает то, о чем вы просите.

А для получения дополнительной справочной информации ознакомьтесь с сообщением Колина Мика, в котором показано, как посетить и заменить Вызывать выражения с выражениями, которые EF может обрабатывать.

Надеюсь это поможет

Алекс

person Alex James    schedule 30.03.2010
comment
Хм... Сообщение Дэмиена зависит от размещения .WithTranslations() в конце выражения, но это снова невозможно в моем случае, потому что после извлечения объекта статьи я не получаю IQueryable, например. для article.Comments и поскольку .WithTranslations является расширением для IQueryable, это не поможет. Скажи мне, если я что-то упустил из виду. - person letmaik; 31.03.2010

Кажется, я нашел ответ на свою "проблему". Моя проблема на самом деле была не проблемой, а скорее неправильным мышлением.

Когда я прочитал это Мне стало ясно, что я просто хотел найти решение неправильной проблемы. Позвольте мне объяснить, что ....

Я использую шаблон POCO с EF4, и отложенная загрузка все еще активирована. С этим я мог волшебным образом пройтись по объектам, не отправляя дополнительных запросов, по крайней мере, они не были видны в моем коде. Но, конечно, для каждого доступа к отношениям отправлялись запросы, и я также наблюдал за этим с помощью EF Profiler. И, конечно же, я также хотел, чтобы они были оптимальными, т. Е. «Использовать условие where в этом sql» вместо «извлекать все строки и затем выполнять фильтрацию». Такой образ мышления также называют преждевременной оптимизацией.

В конце концов меня осенило, когда я начал думать: «Что было бы, если бы я не использовал ленивую загрузку?» Просто, все данные, которые мне нужны, извлекаются в первую очередь, а затем вы работаете с этими данными и ни с чем другим, то есть без скрытых запросов. Если посмотреть на это таким образом, имеет смысл использовать ICollection вместо IQueryable для отношений объекта домена. И, конечно же, я не могу использовать Expression> для вызова .Where() в ICollection, а скорее внутреннюю функцию Func‹..>.

Чтобы ответить на мой собственный вопрос, Expression‹> следует использовать только при наличии доступа к репозиторию или осведомленности о нем, чего нет у объектов POCO. Если они должны использоваться снаружи, то есть в ICollection, они должны быть скомпилированы в объекты Func следующим образом:

IEnumerable<Comment> comments =
  (from c in article.Comments
   select c).Where(CommentExpressions.IsApproved.Compile());

И если действительно требуется высокая производительность, то необходимо попросить репозиторий получить все комментарии, относящиеся к этой статье, путем сопоставления идентификатора и того, где выполняется CommentExpressions.IsApproved. Что-то подобное:

IEnumerable<Comment> comments =
  (from c in dataContainer.ArticleComments
   where c.ArticleId == article.Id
   select c).Where(CommentExpressions.IsApproved);

Теперь последнее условие where сохраняет выражение из-за отсутствия .compile() и может использоваться в sql.

Меня это почти устраивает. Что меня раздражает, так это необходимость вызывать ".compile()", и я до сих пор не понимаю, как я должен построить или позволить одному выражению использовать другое выражение, которое кажется невозможным, кроме как вызовом .compile() включая его, потому что это снова только ICollection, на который я не могу помещать объекты Expression. Я полагаю, что здесь я могу использовать LINQKit, который затем удаляет вызовы compile().

Надеюсь, я иду в правильном направлении. Если вы обнаружите какую-либо логическую ошибку или придумаете, как это сделать лучше, сообщите мне об этом в комментариях, чтобы я мог обновить ответ. Спасибо всем!

person letmaik    schedule 05.04.2010

Поскольку вы используете LinqKit, используйте построитель предикатов:

IEnumerable<Comment> comments =
  article.Comments.Where(CommentExpressions.IsApproved).Select(c=>c);

 public static class CommentExpressions
 {
    public static Expression<Func<Module, bool>> IsApproved
    {
        get
        {
            var pred = PredicateBuilder.True<Module>();
            pred = pred.And(m => m.IsApproved);
            return pred
        }
    }
 }
person Chao    schedule 30.03.2010
comment
У меня не работает, потому что article.Comments.Where() принимает только Func‹Comment,bool›, а не выражение. (Также была опечатка, это не Module, bool, а Comment, bool, извините) - person letmaik; 30.03.2010