我遇到了用LINQ和Lambda编写的查询的麻烦。到目前为止,我得到了很多错误,这是我的代码:
int id = 1;
var query = database.Posts.Join(database.Post_Metas,
post => database.Posts.Where(x => x.ID == id),
meta => database.Post_Metas.Where(x => x.Post_ID == id),
(post, meta) => new { Post = post, Meta = meta });
我不确定这个问题是否正确。
您的键选择器不正确。它们应该接受与所讨论的表类型相同的对象,并返回在连接中使用的键。我想你的意思是:
var query = database.Posts.Join(database.Post_Metas,
post => post.ID,
meta => meta.Post_ID,
(post, meta) => new { Post = post, Meta = meta });
您可以随后应用where子句,而不是作为键选择器的一部分。
我发现如果你熟悉SQL语法,使用LINQ查询语法会更清晰、更自然,并且更容易发现错误:
var id = 1;
var query =
from post in database.Posts
join meta in database.Post_Metas on post.ID equals meta.Post_ID
where post.ID == id
select new { Post = post, Meta = meta };
如果您真的坚持使用lambdas,那么您的语法就有很大的问题。下面是相同的查询,使用LINQ扩展方法:
var id = 1;
var query = database.Posts // your starting point - table in the "from" statement
.Join(database.Post_Metas, // the source table of the inner join
post => post.ID, // Select the primary key (the first part of the "on" clause in an sql "join" statement)
meta => meta.Post_ID, // Select the foreign key (the second part of the "on" clause)
(post, meta) => new { Post = post, Meta = meta }) // selection
.Where(postAndMeta => postAndMeta.Post.ID == id); // where statement
因为当我开始LINQ + EntityFramework时,我盯着这些例子看了一天。
如果你正在使用EntityFramework,并且你在Post模型对象上设置了一个名为Meta的导航属性,这非常简单。如果你用的是实体,没有导航属性,还在等什么?
database
.Posts
.Where(post => post.ID == id)
.Select(post => new { post, post.Meta });
如果你先做代码,你可以这样设置属性:
class Post {
[Key]
public int ID {get; set}
public int MetaID { get; set; }
public virtual Meta Meta {get; set;}
}
这件事有两种解释。使用LINQPad(如果你是LINQ新手,这是无价的)和一个虚拟数据库,我构建了以下查询:
Posts.Join(
Post_metas,
post => post.Post_id,
meta => meta.Post_id,
(post, meta) => new { Post = post, Meta = meta }
)
or
from p in Posts
join pm in Post_metas on p.Post_id equals pm.Post_id
select new { Post = p, Meta = pm }
在这种特殊情况下,我认为LINQ语法更清晰(我在两者之间进行更改,这取决于哪个更容易阅读)。
不过,我想指出的是,如果你的数据库中有适当的外键(在post和post_meta之间),那么你可能不需要显式连接,除非你试图加载大量的记录。您的示例似乎表明您正在尝试加载单个帖子及其元数据。假设每个post都有很多post_meta记录,那么你可以这样做:
var post = Posts.Single(p => p.ID == 1);
var metas = post.Post_metas.ToList();
如果您希望避免n+1问题,那么您可以显式地告诉LINQ to SQL一次性加载所有相关项(尽管对于您更熟悉L2S的人来说,这可能是一个高级主题)。下面的例子说的是“当你加载一个Post时,也通过'Post_metas'属性表示的外键加载与它相关的所有记录”:
var dataLoadOptions = new DataLoadOptions();
dataLoadOptions.LoadWith<Post>(p => p.Post_metas);
var dataContext = new MyDataContext();
dataContext.LoadOptions = dataLoadOptions;
var post = Posts.Single(p => p.ID == 1); // Post_metas loaded automagically
可以对同一类型或许多不同类型的单个DataLoadOptions集进行多个LoadWith调用。如果你经常这样做,你可能只是想考虑缓存。