Docs 菜单
Docs 主页
/ / /
C#/.NET
/

LINQ

在此页面上

  • Overview
  • 将集合设为可查询
  • 支持的聚合阶段
  • $project
  • $match
  • $limit
  • $skip
  • $unwind
  • $group
  • $sort
  • $lookup
  • 不支持的聚合阶段
  • 支持的方法

在本指南中,您可以学习如何将 LINQ 与 MongoDB .NET/C# 驱动程序结合使用。LINQ 允许您使用语言关键字和操作符构造针对强类型对象集合的查询。.NET/C# 驱动程序会自动将 LINQ 查询转换为聚合操作

本指南中的示例使用 Atlas 样本数据集所提供的 sample_restaurants 数据库中的 restaurants 集合。要了解如何创建免费的 MongoDB Atlas 集群并加载示例数据集,请参阅快速入门。

以下 RestaurantAddressGradeEntry 类将为该集合中的文档建模:

public class Restaurant
{
public ObjectId Id { get; set; }
public string Name { get; set; }
[BsonElement("restaurant_id")]
public string RestaurantId { get; set; }
public string Cuisine { get; set; }
public Address Address { get; set; }
public string Borough { get; set; }
public List<GradeEntry> Grades { get; set; }
}
public class Address
{
public string Building { get; set; }
[BsonElement("coord")]
public float[] Coordinates { get; set; }
public string Street { get; set; }
[BsonElement("zipcode")]
public string ZipCode { get; set; }
}
public class GradeEntry
{
public DateTime Date { get; set; }
public string Grade { get; set; }
public float Score { get; set; }
}

注意

restaurants 集合中的文档使用驼峰命名约定。本指南中的示例使用 ConventionPack 将集合中的字段反序列化为 Pascal 语句,然后映射到 Restaurant 类中的属性。

如需了解有关自定义序列化的更多信息,请参阅“自定义序列化”。

要使用 LINQ 查询您的集合,必须先创建一个链接到该集合的 iQueryable 对象。要创建对象,请使用 AsQueryable() 方法,如下所示:

var restaurantsCollection = restaurantsDatabase.GetCollection<Restaurant>("restaurants");
var queryableCollection = restaurantsCollection.AsQueryable();

AsQueryable() 方法返回一个 IMongoQueryable 实例,该实例具有 IQueryable 扩展方法以及一组特定于 MongoDB 的方法。

获得可查询对象后,您可以使用方法语法组成查询。一些管道阶段还支持查询理解语法,类似于 SQL 查询语法。

选择 Method Syntax(方法语法)或 Query Syntax(查询语法)标签页,查看如何使用 LINQ 创建查询:

var query = queryableCollection
.Where(r => r.Name == "The Movable Feast")
.Select(r => new { r.Name, r.Address });
var query = from r in queryableCollection
where r.Name == "The Movable Feast"
select new { r.Name, r.Address };

您可以按如下方式输出上述示例的结果:

foreach (var restaurant in query)
{
Console.WriteLine(restaurant.ToJson());
}
{ "name" : "The Movable Feast", "address" : { "building" : "284", "coord" : [-73.982923900000003, 40.6580753], "street" : "Prospect Park West", "zipcode" : "11215" } }

提示

访问查询结果

您还可以使用 ToList()ToCursor() 方法访问查询结果:

var results = query.ToList();
var results = query.ToCursor();

您可以使用 LINQ 来创建聚合管道。.NET/C# 驱动程序会自动将每个 LINQ 语句转换为相应的聚合管道阶段。在本节中,您可以了解哪些聚合管道阶段受到支持。

要了解有关聚合管道阶段的更多信息,请参阅服务器手册中的聚合阶段页面。

$project 聚合阶段返回仅包含指定字段的文档。

选择 Method Syntax(方法语法)或 Query Syntax (查询语法)标签页以查看如何使用 LINQ 生成 $project 阶段:

var query = queryableCollection
.Select(r => new { r.Name, r.Address });
var query = from r in queryableCollection
select new { r.Name, r.Address };

上述示例的结果包含以下文档:

{ "name" : "The Movable Feast", "address" : { "building" : "284", "coord" : [-73.982923900000003, 40.6580753], "street" : "Prospect Park West", "zipcode" : "11215" } }

注意

排除 _id 字段

如果您不在 LINQ 投影中包含 _id 字段,.NET/C# 驱动程序会自动将其从结果中排除。

$match 聚合阶段将返回与指定条件匹配的文档。

选择 Method Syntax(方法语法)或 Query Syntax (查询语法)标签页以查看如何使用 LINQ 生成 $match 阶段:

var query = queryableCollection
.Where(r => r.Name == "The Movable Feast");
var query = from r in queryableCollection
where r.Name == "The Movable Feast"
select r;

上述示例的结果包含以下文档:

// Results Truncated
{ "_id" : ObjectId(...), "name" : "The Movable Feast", "restaurant_id" : "40361606", "cuisine" : "American", "address" : {...}, "borough" : "Brooklyn", "grades" : [...] }

$limit 聚合阶段限制查询返回的文档数量。下面的示例展示了如何使用 LINQ 生成 $limit 阶段:

var query = queryableCollection
.Where(r => r.Cuisine == "Italian")
.Select(r => new {r.Name, r.Cuisine})
.Take(5);

上述示例的结果包含以下文档:

{ "name" : "Philadelhia Grille Express", "cuisine" : "Italian" }
{ "name" : "Isle Of Capri Resturant", "cuisine" : "Italian" }
{ "name" : "Marchis Restaurant", "cuisine" : "Italian" }
{ "name" : "Crystal Room", "cuisine" : "Italian" }
{ "name" : "Forlinis Restaurant", "cuisine" : "Italian" }

$skip 聚合阶段会跳过查询返回的指定数量的文档,并返回其余结果。下面的示例展示了如何使用 LINQ 生成 $skip 阶段:

var query = queryableCollection
.Where(r => r.Cuisine == "Italian")
.Select(r => new {r.Name, r.Cuisine})
.Skip(2);

前面的示例跳过了符合条件的前两家餐厅,并返回其余的餐厅。结果包含以下文档:

// Results Truncated
{ "name" : "Marchis Restaurant", "cuisine" : "Italian" }
{ "name" : "Crystal Room", "cuisine" : "Italian" }
{ "name" : "Forlinis Restaurant", "cuisine" : "Italian" }
...

$unwind 聚合阶段解构指定的数组字段,并为该数组中的每个元素返回一个文档。

选择 Method Syntax(方法语法)或 Query Syntax(查询语法)标签页以查看如何使用 LINQ 生成 $unwind 阶段:

var query = queryableCollection
.Where(r => r.Name == "The Movable Feast")
.SelectMany(r => r.Grades);
var query = from r in queryableCollection
where r.Name == "The Movable Feast"
from grade in r.Grades
select grade;

上述示例中的查询会查找 Name 字段具有“The Movable Feast”值的文档。然后,对于此文档的 Grades 数组中的每个元素,该查询均会返回一个新文档。结果包含以下文档:

{ "date" : ISODate("2014-11-19T00:00:00Z"), "grade" : "A", "score" : 11 }
{ "date" : ISODate("2013-11-14T00:00:00Z"), "grade" : "A", "score" : 2 }
{ "date" : ISODate("2012-12-05T00:00:00Z"), "grade" : "A", "score" : 13 }
{ "date" : ISODate("2012-05-17T00:00:00Z"), "grade" : "A", "score" : 11 }

$group 聚合阶段会根据您指定的条件将文档分为多个群组。

选择 Method Syntax(方法语法)或 Query Syntax(查询语法)标签页以查看如何使用 LINQ 生成 $group 阶段:

var query = queryableCollection
.GroupBy(r => r.Cuisine)
.Select(g => new { Cuisine = g.Key, Count = g.Count() });
var query = from r in queryableCollection
group r by r.Cuisine into g
select new {Cuisine = g.Key, Count = g.Count()};

前面的示例按每个文档的Cuisine字段中的值对各文档进行分组,然后计算具有每个Cuisine值的文档数量。结果包含以下文件:

// Results Truncated
{ "cuisine" : "Caribbean", "count" : 657 }
{ "cuisine" : "Café/Coffee/Tea", "count" : 1214 }
{ "cuisine" : "Iranian", "count" : 2 }
{ "cuisine" : "Nuts/Confectionary", "count" : 6 }
{ "cuisine" : "Middle Eastern", "count" : 168 }
...

注意

结果顺序

上述查询并非总以同一顺序返回结果。运行此示例所返回结果的顺序可能与上述顺序不同。

$sort聚合阶段按您指定的顺序返回查询结果。

选择 Method Syntax(方法语法)或 Query Syntax(查询语法)标签页以查看如何使用 LINQ 生成 $sort 阶段:

var query = queryableCollection
.OrderBy(r => r.Name)
.ThenByDescending(r => r.RestaurantId);
var query = from r in queryableCollection
orderby r.Name, r.RestaurantId descending
select r;

上例按 Name 字段的字母顺序返回查询结果,并对 RestaurantId 字段进行二级降序排序。以下是返回结果中包含的文档的子集:

// Results Truncated
...
{ "_id" : ObjectId(...), "name" : "Aba Turkish Restaurant", "restaurant_id" : "41548686", "cuisine" : "Turkish", "address" : {...}, "borough" : "Manhattan", "grades" : [...] }
{ "_id" : ObjectId(...), "name" : "Abace Sushi", "restaurant_id" : "50006214", "cuisine" : "Japanese", "address" : { ... }, "borough" : "Manhattan", "grades" : [...] }
{ "_id" : ObjectId(...), "name" : "Abacky Potluck", "restaurant_id" : "50011222", "cuisine" : "Asian", "address" : { ... }, "borough" : "Manhattan", "grades" : [...] }
{ "_id" : ObjectId(...), "name" : "Abaleh", "restaurant_id" : "50009096", "cuisine" : "Mediterranean", "address" : { ... }, "borough" : "Manhattan", "grades" : [...] }
...

$lookup 聚合阶段将同一数据库中一个集合中的文档与另一集合中的文档连接起来。$lookup 阶段向每个输入文档添加一个新的数量字段。新的大量字段包含来自“已加入”收集的匹配文档。

注意

要执行查找,必须使用 AsQueryable 方法使两个集合都可进行查询。

要了解如何让集合可供查询,请参阅让集合可供查询

sample_restaurants 数据库中名为 reviews 的第二个集合(其中包含餐厅评价)为例。您可以使用 $lookup 阶段将该集合中的文档联接到 restaurants 集合中具有相同 name 值的文档。

以下 Review 类对 reviews 集合中的文档进行建模:

public class Review
{
public ObjectId Id { get; set; }
[BsonElement("restaurant_name")]
public string RestaurantName { get; set; }
public string Reviewer { get; set; }
[BsonElement("review_text")]
public string ReviewText { get; set; }
}

选择 Method Syntax(方法语法)或 Query Syntax (查询语法)标签页以查看如何使用 LINQ 生成 $lookup 阶段:

var query = queryableCollection
.GroupJoin(reviewCollection,
restaurant => restaurant.Name,
review => review.RestaurantName,
(restaurant, reviews) =>
new { Restaurant = restaurant, Reviews = reviews }
);
var query = from restaurant in queryableCollection
join rv in reviewCollection on restaurant.Name equals rv.RestaurantName into reviews
select new { restaurant, reviews };

上述示例返回来自 restaurants 集合的所有文档。每个餐厅文档都添加了一个名为 reviews 的字段,其中包含该餐厅的所有评论。如果评论文档中的 name 字段的值与餐厅文档的 name 字段匹配,则评论与餐厅匹配。

以下是返回结果的一个子集:

// Results Truncated
{ "restaurant" : {
"_id" : ObjectId("..."),
"name" : "The Movable Feast",
"restaurant_id" : "40361606",
"cuisine" : "American",
"address" : {...},
"borough" : "Brooklyn",
"grades" : [...] },
"reviews" : [
{ "_id" : ObjectId(...), "restaurant_name" : "The Movable Feast", "reviewer" : "Lazlo Cravensworth", "review_text" : "Great restaurant! 12/10 stars!" },
{ "_id" : ObjectId("..."), "restaurant_name" : "The Movable Feast", "reviewer" : "Michael Scarn", "review_text" : "It really was a feast" }
]
}

LINQ 的 MongoDB .NET/C# Driver 实现不支持以下聚合阶段:

  • $redact

  • $geoNear

  • $out

以下是 LINQ 的 MongoDB .NET/C# 驱动程序实施所支持的部分方法:

方法名称
说明
Any
确定是否有文档符合指定条件
Average
计算指定字段的平均值
Count
返回 Int32,表示符合指定条件的文档数量
LongCount
返回 Int64,表示符合指定条件的文档数量
Distinct
返回符合指定条件的不同文档
First
返回第一个匹配的文档,如果未找到,则引发异常
FirstOrDefault
返回第一个匹配的文档,如果未找到,则返回 null
GroupBy
根据指定标准对文档分组
GroupJoin
对同一数据库中的另一个集合执行左外连接
Max
返回具有最大指定值的文档
OfType
返回与指定类型匹配的文档
OrderBy, OrderByDescending
按指定的排序顺序返回结果
ThenBy, ThenByDescending
允许指定辅助排序
Select
根据指定的条件选择文档
SelectMany
投影序列中的每个元素,并将生成的序列合并到一个文档中
Single
返回唯一匹配的文档,如果没有完全匹配的文档,则抛出异常
SingleOrDefault
返回匹配的文档,如果没有匹配的文档,则返回 null
Skip
跳过指定数量的文档,并返回其余结果
Sum
返回指定字段中值的总和
Take
指定要返回的结果数
Where
返回符合指定条件的所有文档

后退

聚合(Aggregation)