能否通过特性配置将C#对象属性存入MongoDB的两个字段?
实现单个C#属性映射到MongoDB两个字段的方案
要实现将User类的Name属性同时存储为MongoDB中的Name(原始值)和_sortingName(小写值)字段,且通过特性方式完成、无需聚合语句,以下是两种可行方案:
方案一:属性同步+MongoDB原生特性
这种方式简单直接,通过属性的setter同步更新排序字段,配合MongoDB驱动的特性完成字段映射与索引配置。
修改后的User类
public class User { private string _name = "Bob"; [BsonElement("Name")] public string Name { get => _name; set { _name = value; // 同步设置小写排序字段 _sortingName = value?.ToLowerInvariant(); } } [BsonElement("_sortingName")] // 为_sortingName建立升序索引 [BsonIndexDefinition("{ _sortingName: 1 }")] public string _sortingName { get; private set; } = "bob"; public int Age { get; set; } = 33; }
说明
- 通过
Name属性的setter,在赋值时自动将小写值同步到_sortingName,确保两个字段值始终一致; [BsonElement]特性指定属性对应的MongoDB字段名;[BsonIndexDefinition]特性直接为_sortingName配置索引,无需额外的索引创建代码;- 写入MongoDB时会自动生成目标格式的文档,查询排序时直接使用
_sortingName字段即可:
var sortedUsers = collection.Find(Builders<User>.Filter.Empty) .Sort(Builders<User>.Sort.Ascending("_sortingName")) .ToList();
方案二:自定义序列化特性(无额外属性暴露)
如果不想在类中暴露_sortingName属性,可以通过自定义特性和序列化器,自动生成并写入排序字段,反序列化时忽略该字段。
步骤1:定义自定义特性
[AttributeUsage(AttributeTargets.Property)] public class LowercaseSortFieldAttribute : Attribute { public string SortFieldName { get; } public LowercaseSortFieldAttribute(string sortFieldName) { SortFieldName = sortFieldName; } }
步骤2:实现自定义序列化器
public class LowercaseSortFieldSerializer<T> : IBsonSerializer<T> { private readonly IBsonSerializer<T> _baseSerializer; private readonly string _sortFieldName; public LowercaseSortFieldSerializer(IBsonSerializer<T> baseSerializer, string sortFieldName) { _baseSerializer = baseSerializer; _sortFieldName = sortFieldName; } public Type ValueType => typeof(T); public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, T value) { // 先序列化原始属性 _baseSerializer.Serialize(context, args, value); // 若属性值为字符串,追加小写排序字段 if (value is string strValue) { var writer = context.Writer; writer.WriteName(_sortFieldName); writer.WriteString(strValue.ToLowerInvariant()); } } public T Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { // 反序列化时移除排序字段,仅处理原始属性 var document = BsonDocumentSerializer.Instance.Deserialize(context, args); document.Remove(_sortFieldName); using var reader = new BsonDocumentReader(document); var newContext = BsonDeserializationContext.CreateRoot(reader); return _baseSerializer.Deserialize(newContext, args); } }
步骤3:配置User类与序列化器
public class User { [LowercaseSortField("_sortingName")] [BsonSerializer(typeof(LowercaseSortFieldSerializer<string>), "_sortingName")] public string Name { get; set; } = "Bob"; public int Age { get; set; } = 33; }
步骤4:注册序列化器与创建索引
在程序启动时配置MongoDB驱动,并为_sortingName创建索引:
// 注册类映射 BsonClassMap.RegisterClassMap<User>(cm => { cm.AutoMap(); var nameMemberMap = cm.MapMember(u => u.Name); nameMemberMap.SetSerializer(new LowercaseSortFieldSerializer<string>( BsonSerializer.LookupSerializer<string>(), "_sortingName" )); }); // 为集合创建_sortingName索引 var collection = mongoDatabase.GetCollection<User>("Users"); collection.Indexes.CreateOne(new CreateIndexModel<User>( Builders<User>.IndexKeys.Ascending("_sortingName") ));
说明
- 该方案无需在
User类中添加额外属性,序列化时自动生成_sortingName字段; - 反序列化时会忽略
_sortingName字段,不影响C#对象的结构; - 同样支持直接使用
_sortingName字段进行排序操作,无需聚合语句。
内容的提问来源于stack exchange,提问作者Roman Matkovskyi
相关产品推荐
相关产品推荐

