У простого linq to sql нет поддерживаемого перевода в SQL - PullRequest
21 голосов
/ 02 декабря 2008

У меня есть это в моем BlogRepository

public IQueryable<Subnus.MVC.Data.Model.Post> GetPosts()
    {
        var query = from p in db.Posts
                    let categories = GetCategoriesByPostId(p.PostId)
                    let comments = GetCommentsByPostId(p.PostId)
                    select new Subnus.MVC.Data.Model.Post
                    {
                        Categories = new LazyList<Category>(categories),
                        Comments = new LazyList<Comment>(comments),
                        PostId = p.PostId,
                        Slug = p.Slug,
                        Title = p.Title,
                        CreatedBy = p.CreatedBy,
                        CreatedOn = p.CreatedOn,
                        Body = p.Body
                    };
        return query;
    }

и

public IQueryable<Subnus.MVC.Data.Model.Comment> GetCommentsByPostId(int postId)
    {
        var query = from c in db.Comments
                    where c.PostId == postId
                    select new Subnus.MVC.Data.Model.Comment
                    {
                        Body = c.Body,
                        EMail = c.EMail,
                        Date = c.CreatedOn,
                        WebSite = c.Website,
                        Name = c.Name
                    };

        return query;
    }

private IQueryable<Subnus.MVC.Data.Model.Category> GetCategoriesByPostId(int postId)
    {
        var query = from c in db.Categories
                    join pcm in db.Post_Category_Maps on c.CategoryId equals pcm.CategoryId
                    where pcm.PostId == postId
                    select new Subnus.MVC.Data.Model.Category
                    {
                        CategoryId = c.CategoryId,
                        Name = c.Name
                    };
        return query;
    }

и когда я применяю этот фильтр

namespace Subnus.MVC.Data
{
 public static class BlogFilters
 {
    public static IQueryable<Post> WherePublicIs(this IQueryable<Post> qry,bool state)
    {

        return from p in qry
               where p.IsPublic == state
               select p;
    }
 }

}

все это находится в одном и том же пространстве имен, если это поможет пространству имен Subnus.MVC.Data

когда я пытаюсь это сделать

public class BlogService : IBlogService
{
...
    public IList<Post> GetPublicPosts()
    {
         return repository.GetPosts().WherePublicIs(true).ToList();
    }
 ...
 }

, который находится в пространстве имен Subnus.MVC.Service выдает ошибку

Method 'System.Linq.IQueryable`1[Subnus.MVC.Data.Model.Comment] GetCommentsByPostId(Int32)' has no supported translation to SQL.

1 Ответ

25 голосов
/ 02 декабря 2008

Вы звоните GetCommentsByPostId в пределах того, что в конечном итоге является деревом выражений. Это дерево, когда оно составлено в BlogService.GetPublicPosts, преобразуется в SQL.

Во время этого преобразования это просто вызов метода, не более того. Linq to Sql понимает определенные вызовы методов, и вы не один из них. Отсюда и ошибка.

На первый взгляд, это должно сработать. Вы пишете многоразовые запросы и составляете их из других запросов. Однако на самом деле вы говорите: «во время обработки каждой строки на сервере базы данных вызывайте этот метод», чего он явно не может сделать. Тот факт, что он принимает IQueryable<T> и возвращает IQueryable<T>, не делает его особенным.

Подумайте об этом так: вы передаете postId на GetCategoriesByPostId. Вы не можете вызывать этот метод, пока у вас не будет postId, и у вас его не будет, пока вы не окажетесь на сервере в запросе.

Возможно, вам потребуется определить общие Expression<> экземпляры для подзапросов и использовать их в композиции. Я не думал о том, как это будет выглядеть, но это, безусловно, выполнимо.

Edit:

Если вы замените

let categories = GetCategoriesByPostId(p.PostId)
let comments = GetCommentsByPostId(p.PostId)
...
Categories = new LazyList<Category>(categories),
Comments = new LazyList<Comment>(comments),

с

Categories = new LazyList<Category>(GetCategoriesByPostId(p.PostId)),
Comments = new LazyList<Comment>(GetCommentsByPostId(p.PostId)),

запрос больше не будет генерировать исключение.

Это потому, что let объявляет переменные диапазона, которые находятся в области видимости для каждой строки. Они должны быть рассчитаны на сервере.

Проекции, однако, позволяют вам помещать произвольный код в присваивания, который затем выполняется при построении результатов на клиенте. Это означает, что будут вызваны оба метода, каждый из которых выдаст свой собственный запрос.

...