简单的linq to sql没有支持的SQL转换

时间:2008-12-02 00:20:21

标签: .net linq-to-sql c#-3.0

我在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 个答案:

答案 0 :(得分:25)

您在最终的表达式树中调用GetCommentsByPostId。该树在BlogService.GetPublicPosts中编写时,将转换为SQL。

在转换期间,它只是一个方法调用,仅此而已。 Linq to Sql理解某些方法调用,而你的不是其中之一。因此错误。

从表面上看,这似乎应该有效。您编写可重用查询并从其他查询中编写它们。但是,你实际上说的是:“在处理数据库服务器上的每一行时,调用这个方法”,这显然是做不到的。它需要IQueryable<T>并返回IQueryable<T>这一事实并不会使它变得特别。

以这种方式思考:您将postId传递给GetCategoriesByPostId。在拥有postId之前,您无法调用该方法,并且在查询服务器上之前没有其中一种方法。

您可能需要为子查询定义公共Expression<>实例,并使用组合中的实例。我没有想过这会是什么样子,但它肯定是可行的。

修改

如果您更换

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声明了范围变量,这些变量在每行的范围内。他们必须在服务器上计算。

然而,预测允许您在任务中放置任意代码,然后在客户端上构建结果时执行。这意味着将调用这两种方法,每种方法都会发出自己的查询。