Простой 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.Datap >
когда я пытаюсь сделать это
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;
}