LINQ
Overview
在本指南中,您可以学习如何将 LINQ 与 MongoDB .NET/C# 驱动程序结合使用。LINQ 允许您使用语言关键字和操作符构造针对强类型对象集合的查询。.NET/C# 驱动程序会自动将 LINQ 查询转换为聚合操作。
本指南中的示例使用 Atlas 样本数据集所提供的 sample_restaurants
数据库中的 restaurants
集合。要了解如何创建免费的 MongoDB Atlas 集群并加载示例数据集,请参阅快速入门。
以下 Restaurant
、Address
和 GradeEntry
类将为该集合中的文档建模:
public class Restaurant { public ObjectId Id { get; set; } public string Name { get; set; } [ ] 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; } [ ] public float[] Coordinates { get; set; } public string Street { get; set; } [ ] 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
$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
$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
$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
$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
$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
$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
$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
聚合阶段将同一数据库中一个集合中的文档与另一集合中的文档连接起来。$lookup
阶段向每个输入文档添加一个新的数量字段。新的大量字段包含来自“已加入”收集的匹配文档。
注意
要执行查找,必须使用 AsQueryable
方法使两个集合都可进行查询。
要了解如何让集合可供查询,请参阅让集合可供查询。
以 sample_restaurants
数据库中名为 reviews
的第二个集合(其中包含餐厅评价)为例。您可以使用 $lookup
阶段将该集合中的文档联接到 restaurants
集合中具有相同 name
值的文档。
以下 Review
类对 reviews
集合中的文档进行建模:
public class Review { public ObjectId Id { get; set; } [ ] public string RestaurantName { get; set; } public string Reviewer { get; set; } [ ] 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 | 返回符合指定条件的所有文档 |