简要说明
你的问题中看起来比较棘手的部分似乎是如何以与 LINQ to SQL(以下简称“L2S”)查询对匿名类所做的方式填充自定义类。
根据你的foreach
循环,我猜测你的自定义类类似于这些:
public class PostType
{
public int PostId { get; set; }
public List<PostComment> PostComments { get; set; }
}
public class PostComment
{
public int CommentId { get; set; }
public string Title { get; set; }
}
LINQ查询应该等同于这个T-SQL语句:
SELECT P.post_id, C.id, C.title
FROM post As P, comment As C
WHERE
P.post_id = @PostId
AND P.post_isdeleted = 0
AND C.CommentPostID = P.post_id
与L2S版本不同(有关详细解释,请参阅下面的“详细说明”部分),此语句返回一个扁平化结果,每行包含P.post_id
、C.id
和C.title
。如果您的PostType
类以相同的方式表示一个条目,则这个问题很容易解决(我并不提倡这样的设计;我只是评论它如何影响它的填充方式)。类中的层次关系改变了事情的性质。
此外,您的代码显示了一个List<PostType>
,但列表并非必需,因为始终只会有一个PostType
,因为您正在过滤post_id
。如果删除该条件,那么您可能会得到具有不同PostIds但满足其他条件的多个匹配项。如果是这种情况,则下面的代码需要更改。
话虽如此,让我们进入一些ADO.NET,并使用SqlDataReader填充类。
int postIdInput = 42;
PostType postType = new PostType()
{
PostId = postIdInput,
PostComments = new List<PostComment>()
};
string sqlStatement = @"SELECT P.post_id As PostId, C.id As CommentId, C.title As Title
FROM post As P, comment As C
WHERE
P.post_id = @PostId
AND P.post_isdeleted = 0 -- 0 is false
AND C.CommentPostID = P.post_id";
string sqlConnectionString = "...";
using (SqlConnection conn = new SqlConnection(sqlConnectionString))
{
conn.Open();
SqlCommand command = new SqlCommand(sqlStatement, conn);
command.Parameters.AddWithValue("@PostId", postIdInput);
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
int commentId = Int32.Parse(reader["CommentId"].ToString());
string title = reader["Title"].ToString();
PostComment postComment = new PostComment
{
CommentId = commentId,
Title = title
};
postType.PostComments.Add(postComment);
}
}
这应该涵盖了您的情况。但是,如果您想要更详细的答案,请继续阅读!
详细解释(也称为“授人以鱼不如授人以渔”)
假设您无法弄清楚如何获取等效的SQL语句。虽然有不同的方法可以做到这一点,但我将集中讨论您正在使用L2S并探索一些相关选项。
步骤1:通过“作弊”将LINQ查询转换为SQL
您很幸运,因为有一个捷径。将现有的LINQ表达式转换为SQL比反向翻译SQL到LINQ更方便。
您可以通过使用以下任一DataContext选项从代码中获取已翻译的T-SQL语句:
注意:我说过这是一种捷径。了解SQL是很好的,但要明确的是,我并不建议盲目使用生成的输出。尽管有时SQL可能与您预期的不同,但它仍然提供了一个不错的起点。如果需要,您可以进行微调。
使用以下任一方法并复制结果 - 您将需要它进行第二步。
示例DataContext.GetCommand()用法:
var query = /* your L2S query here */;
string sqlStatement = context.GetCommand(query).CommandText; // voila!
获取结果的方法有三种:设置断点并复制其值、在即时窗口中查看或在某个位置显示(Console.WriteLine等)。
例如 DataContext.Log 的用法:
context.Log = Console.Out
在该上下文中执行的查询将其SQL语句转储到控制台窗口。您可以从那里复制它。要将它们转储到其他位置,例如调试输出窗口,请查看以下链接:
第二步:使用手头的SQL语句在ADO.NET中使用它
既然您有了SQL语句,我们就可以在ADO.NET中使用它。当然,您也可以使用存储过程,而且应该很容易进行替换。
但在使用之前,您可能需要清理一下语句。我在本地使用类似的查询来获取这个,并且您生成的语句可能类似于此:
SELECT [t0].[post_id], [t1].[id], [t1].[title], (
SELECT COUNT(*)
FROM [comment] AS [t2]
WHERE [t2].[id] = [t0].[post_id]
) As [value]
FROM [post] As [t0]
LEFT OUTER JOIN [comment] As [t1] ON [t1].[CommentPostID] = [t0].[post_id]
WHERE ([t0].[post_id] = @p0) AND ([t0].[post_isdeleted] = 0)
ORDER BY [t0].[post_id], [t1].[id]
注意嵌入的SELECT COUNT(*)语句吗?L2S 查询从未请求计数,但结果请求联接中使用的相等 ID 的计数。还要注意,列没有别名。您将基于它们的实际名称引用列(例如,post_id 对 PostId)。此外,SQL 参数命名为 @p0...@pn 并应用默认排序顺序。您可以将其复制/粘贴到先前使用的SqlDataReader中,但需要重命名列和参数以匹配。
以下是上述内容的清理版本,其中已重命名参数并注释掉不必要的部分(如果采用此方法,请进行测试以确保与预期的结果相同):
SELECT [P].[post_id] As PostId, [C].[id] As CommentId, [C].[title] As Title
FROM [post] As [P]
LEFT OUTER JOIN [comment] As [C] ON [C].[CommentPostID] = [P].[post_id]
WHERE ([P].[post_id] = @PostId) AND ([P].[post_isdeleted] = 0)
现在可以将上述内容与之前的SqlDataReader一起使用。
如果L2S查询采用SelectMany格式,可以生成更直接的查询,例如:
var query = from arow in context.post
from c in context.comment
where arow.post_id == id && arow.post_isdeleted == false
&& c.CommentPostID == arow.post_id
select new
{
arow.post_id,
c.id,
c.title
};
SelectMany L2S查询生成类似于以下SQL语句:
SELECT [t0].[post_id], [t1].[id], [t1].[title]
FROM [post] As [t0], [comment] As [t1]
WHERE ([t0].[post_id] = @p0) AND ([t0].[post_isdeleted] = 0)
AND ([t1].[CommentPostID] = [t0].[post_id])
LINQPad
虽然这个详细的解释可能会让人感到不知所措,但有一个简单的方法可以让您随时掌握这些信息。如果您还没有尝试过LINQPad,那么我强烈推荐它 - 它还是免费的! LINQPad将显示您的L2S查询结果,具有SQL选项卡以查看生成的SQL,并显示使用的lambda表达式(上面的查询语法显示为lambda /扩展等效)。除此之外,它还是一个用于通用C#/VB.NET(包括LINQ to Objects/XML)和SQL编码的优秀工具,具有数据库支持和更多功能。
以下是LINQPad的微小截图,显示了一些早期讨论的主题:
![LINQPad](https://imgur.com/JmRZ4s.jpg)
我不想占用更多的页面空间,所以点击此处查看原始尺寸的图片。
如果你看到这里了,恭喜你!:)