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

Я хочу выделить часто используемые выражения в запросах 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. *

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

Есть идеи или лучшие практики? Спасибо большое! нео

Ответы [ 3 ]

1 голос
/ 30 марта 2010

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

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

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

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

Alex

1 голос
/ 06 апреля 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 сохраняет Expression из-за отсутствия .compile () и может быть использовано в sql.

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

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

0 голосов
/ 30 марта 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
        }
    }
 }
...