在 MongoDB 中使用 C# Record 类型
Source: Dev.to
这篇客座作者文章由Markus Wildgruber – Managing Director/Cloud Architect, sevantage software GmbH
Introduction
当 C# 9.0 在一段时间前推出时,主要关注点是对不可变类型的更好支持。这种编程模式旨在提升并发执行的支持,并因副作用的可能性降低而使代码更易于理解。虽然在此之前你也可以手动定义不可变类型,但 C# 9.0 引入了 record 关键字,使得定义不可变类型变得轻而易举。这种“语法糖”还为类型添加了以下功能:
record类型实现了IEquatable以及各种处理值相等性的其他方法——不再需要手动实现这些方法。即使你在应用代码中很少通过所有属性比较 MongoDB 文档,只要能够将对象整体比较而不是逐个属性检查,单元测试的可读性也会大幅提升。record类型基于属性值实现了GetHashCode。由于大多数record类型只使用不可变的定位参数,哈希码在对象初始化后也不会改变。这在你将对象用作字典键或加入哈希集合时尤为重要。它可以避免因哈希码与对象值不匹配而导致项目从集合中消失的难以追踪的 bug。record的ToString方法会返回对象属性的有意义显示——这是一项小改动,却在调试时帮助巨大!
欲了解 record 类型特性的完整概览,请阅读 Microsoft 网站上的文档。
定义记录 POCO
相较于传统的 class 类型,使用 record 定义还有一个额外的好处——代码更简洁:
using MongoDB.Bson;
public record Movie(
ObjectId Id,
string Title,
string Plot
);
如上示例所示,大多数 record 类型使用主构造函数,直接在括号中列出属性。编译器会把这种定义转换为类似下面的类:
using MongoDB.Bson;
public class Movie : IEquatable
{
public Movie(ObjectId id, string title, string plot)
{
Id = id;
Title = title;
Plot = plot;
}
public ObjectId Id { get; init; }
public string Title { get; init; }
public string Plot { get; init; }
// …
}
声明式映射
使用这种方法时,必须调整 MongoDB 属性的声明式映射,使其应用于生成的属性。例如,如果希望在 MongoDB 中将 Id 存储为 ObjectId,但在模型中将其暴露为 string,则需要使用 property 属性目标说明符:
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
[BsonIgnoreExtraElements]
public record Movie(
[property: BsonRepresentation(BsonType.ObjectId)] string Id,
[property: BsonElement("title")] string Title,
[property: BsonElement("plot")] string Plot
);
手动映射
如果您更倾向于手动创建类映射(详细信息请参阅 MongoDB C# Driver 文档),也可以这样做:
BsonClassMap.RegisterClassMap(classMap =>
{
classMap.SetIgnoreExtraElements(true);
classMap.MapMember(p => p.Id)
.SetSerializer(new StringSerializer(MongoDB.Bson.BsonType.ObjectId));
classMap.MapMember(p => p.Title).SetElementName("title");
classMap.MapMember(p => p.Plot).SetElementName("plot");
});
综合示例
以下基于文件的 C# 应用程序展示了在访问基于 record 类型的 MongoDB 数据时无需进行任何更改:
#:package MongoDB.Driver@*
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
string connStr = "mongodb://localhost:27017";
string databaseName = "sample_mflix";
int limit = 5;
IMongoClient client = new MongoClient(connStr);
IMongoDatabase database = client.GetDatabase(databaseName);
IMongoCollection moviesCollection = database.GetCollection("movies");
foreach (Movie movie in moviesCollection
.Find(FilterDefinition.Empty)
.Limit(limit)
.ToEnumerable())
{
Console.WriteLine(movie);
}
[BsonIgnoreExtraElements]
public record Movie(
[property: BsonRepresentation(BsonType.ObjectId)] string Id,
[property: BsonElement("title")] string Title,
[property: BsonElement("plot")] string Plot
);
结论与后续步骤
Records 让基于 MongoDB 的 C# 应用的模型层更简洁、更安全。它们免费提供不可变值语义,消除 boiler‑plate 代码,并提供可靠的相等性、哈希和调试支持。考虑在领域模型中采用 record 类型,并进一步探索 MongoDB 驱动的功能,如自定义序列化器或约定,以充分利用不可变数据结构的优势。
Boilerplate 代码用于相等性检查并添加 ToString 实现。确保使用 property 属性目标将 MongoDB 序列化属性应用于属性。
试一试——将你的一些基于 class 的 POCO 替换为 record。我们很期待你分享使用体验——如果遇到坑或发现有效的模式,请留言,让大家一起学习。
祝编码愉快!
