Простой linq to sql не поддерживает перевод на SQL

У меня это в моем 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;
    }
 }

}

все это в том же пространстве имен, если это пространство имен help Subnus.MVC.Data​​p >

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

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

Вы вызываете 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 объявляет переменные диапазона, которые доступны для каждой строки. Они должны быть рассчитаны на сервере.

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

Ответ 2

Upadate 2: работает

var query = from p in repository.GetPosts()
           where p.Slug == slug
       select p;
return query.SingleOrDefault();

если я создаю этот

public IQueryable<Post> GetPublicPosts()
    {
        var query = from p in db.Posts
                    where p.IsPublic==true
                    select new Subnus.MVC.Data.Model.Post
                    {
                        Categories = new LazyList<Category>(GetCategoriesByPostId(p.PostId)),
                        Comments = new LazyList<Comment>(GetCommentsByPostId(p.PostId)),

                        PostId = p.PostId,
                        Slug = p.Slug,
                        Title = p.Title,
                        CreatedBy = p.CreatedBy,
                        CreatedOn = p.CreatedOn,
                        Body = p.Body
                    };
        return query;
    }

в BlogRepository он работает, но это просто повторение себя, я думаю, потому что я создаю я мой класс, а не linq для sql created class

UPDATE:, потому что тогда он становится linq для объекта, а не linq для sql
если я это сделаю:

public IQueryable<Post> GetPublicPosts()
    {
        var query = from p in GetPosts()
                    where p.IsPublic==true
                    select p;
        return query;
    }