2

我正在尝试将这段非常简单的 SQL 转换为 LINQ:

select * from Projects p
inner join Documents d
    on p.ProjectID = d.ProjectID
left join Revisions r
    on r.DocumentID = d.DocumentID 
    and r.RevisionID IN (SELECT max(r2.RevisionID) FROM Revisions r2 GROUP BY r2.DocumentID) 
WHERE p.ProjectID = 21 -- Query string in code

这表示,如果文档存在任何修订,请返回最高修订 ID。由于它是左连接,如果不存在修订,我仍然希望返回结果。

这按预期工作,显示存在的任何修订(并返回最高修订 ID),所有没有任何修订的文档也是如此。

尝试使用 LINQ 编写此内容时,我只得到文档存在修订的结果。

到目前为止,这是我的尝试:

    var query = from p in db.Projects
                              join d in db.Documents on new { ProjectID = p.ProjectID } equals new { ProjectID = Convert.ToInt32(d.ProjectID) }
                              join r in db.Revisions on new { DocumentID = d.DocumentID } equals new { DocumentID = Convert.ToInt32(r.DocumentID) } into r_join
                              from r in r_join.DefaultIfEmpty()
                              where
                               (from r2 in db.Revisions
                                   group r2 by new { r2.DocumentID }
                                       into g
                                       select new { MaxRevisionID = g.Max(x => x.RevisionID) }).Contains(
                                       new { MaxRevisionID = r.RevisionID }) &&
                                p.ProjectID == Convert.ToInt32(Request.QueryString["projectId"])
                              select new { d.DocumentID, d.DocumentNumber, d.DocumentTitle, RevisionNumber = r.RevisionNumber ?? "<No rev>", Status = r.DocumentStatuse == null ? "<Not set>" : r.DocumentStatuse.Status };

我不太擅长 LINQ,一直在使用转换器“Linqer”来帮助我,但是在尝试时我收到以下消息:

“SQL 无法转换为 LINQ:只能使用 JOIN 表达式中的“=”运算符。“IN”运算符无法转换。”

你会看到我.DefaultIfEmpty()在修订表上。如果我删除了where (进行分组的代码,无论文档是否存在修订,我都会得到所需的结果。但是如果有链接,where 子句应该返回文档的最高修订号,如果没有,我仍然想返回所有其他数据。与我的 SQL 代码不同,这不会发生。它只返回有指向修订表链接的数据。

我希望这有点道理。按代码分组是什么弄乱了我的结果集。无论是否有指向修订表的链接,我仍然希望返回我的结果。请帮忙!

谢谢。

=======

感谢 Gert,我现在使用的代码。

        var query = from p in db.Projects
                    from d in p.Documents
                    where p.ProjectID == Convert.ToInt32(Request.QueryString["projectId"])
                    select new
                               {
                                   p.ProjectID,
                                   d.DocumentNumber,
                                   d.DocumentID,
                                   d.DocumentTitle,
                                   Status = d.Revisions
                                    .OrderByDescending(rn => rn.RevisionID)
                                    .FirstOrDefault().DocumentStatuse.Status,
                                   RevisionNumber = d.Revisions
                                    .OrderByDescending(rn => rn.RevisionID)
                                    .FirstOrDefault().RevisionNumber
                               };

        gvDocumentSelection.DataSource = query;
        gvDocumentSelection.DataBind();

虽然这可行,但您会看到我通过运行相同的代码从修订表中选择了两个字段,但选择了两个不同的字段。我猜有更好,更有效的方法来做到这一点?理想情况下,我想加入修订表,以防我需要访问更多字段,但随后我又遇到了相同的分组问题。

           Status = d.Revisions
            .OrderByDescending(rn => rn.RevisionID)
            .FirstOrDefault().DocumentStatuse.Status,
           RevisionNumber = d.Revisions
            .OrderByDescending(rn => rn.RevisionID)
            .FirstOrDefault().RevisionNumber

最终工作代码:

        var query = from p in db.Projects
                    from d in p.Documents
                    where p.ProjectID == Convert.ToInt32(Request.QueryString["projectId"])
                    select new
                               {
                                   p.ProjectID,
                                   d.DocumentNumber,
                                   d.DocumentID,
                                   d.DocumentTitle,
                                   LastRevision = d.Revisions
                        .OrderByDescending(rn => rn.RevisionID)
                        .FirstOrDefault()
                               };

        var results = from x in query
                      select
                          new
                              {
                                  x.ProjectID,
                                  x.DocumentNumber,
                                  x.DocumentID,
                                  x.DocumentTitle,
                                  x.LastRevision.RevisionNumber,
                                  x.LastRevision.DocumentStatuse.Status
                              };

        gvDocumentSelection.DataSource = results;
        gvDocumentSelection.DataBind();
4

1 回答 1

1

如果您有 1:n 导航属性,则有一种更简单(且推荐)的方法来实现此目的:

from p in db.Projects
from d in p.Documents
select new { p, d,
             LastRevision = d.Revisions
                            .OrderByDescending(r => r.RevisionId)
                            .FirstOrDefault() }

如果没有导航属性,它是类似的:

from p in db.Projects
join d in db.Documents on new { ProjectID = p.ProjectID } 
                   equals new { ProjectID = Convert.ToInt32(d.ProjectID) }
select new { p, d,
             LastRevision = db.Revisions
                 .Where(r => d.DocumentID = Convert.ToInt32(r.DocumentID))
                 .OrderByDescending(r => r.RevisionId)
                 .FirstOrDefault() }

编辑
您可以使用各种投影修改这个非常广泛的基本查询,例如:

from x in query select new { x.p.ProjectName,
                             x.d.DocumentName,
                             x.LastRevision.DocumentStatus.Status,
                             x.LastRevision.FieldA,
                             x.LastRevision.FieldB
                            }
于 2012-12-16T13:28:53.410 回答