使用LINQ查询MongoDB时RuleActions序列化错误求助
问题分析
错误Serializer for ABSRotas.Routing.Entities.Models.Integration does not have a member named RuleActions的核心原因:
Integration实体中,RuleActions是表达式体属性(public IReadOnlyList<RuleAction> RuleActions => _ruleActions;),MongoDB LINQ驱动无法将其识别为可查询的实体成员,驱动的元数据解析逻辑不兼容这种语法糖形式的属性。- 映射代码仅注册了私有字段
_ruleActions的映射,但未将公共的RuleActions属性与该字段关联,导致驱动解析LINQ表达式i.RuleActions.Count时,找不到对应的序列化成员。
当改为List时,驱动默认识别了常规集合类型的属性,错误消失,但破坏了实体的封装性(对外暴露可修改的集合)。
解决方案
步骤1:调整实体属性定义(保持封装性)
将表达式体属性改为常规只读属性,确保驱动能识别为实体成员,同时保持集合的只读性:
using ABSCard.Extensions; using ABSRotas.Common.Domain; namespace ABSRotas.Routing.Entities.Models { public class Integration : Entity, IAggregateRoot { public DateTime CreatedOn { get; private set; } public Guid CreatedBy { get; private set; } public bool Status { get; private set; } public string Name { get; private set; } private List<RuleAction> _ruleActions; // 改为常规只读属性 public IReadOnlyList<RuleAction> RuleActions { get; } protected Integration() { _ruleActions = new List<RuleAction>(); RuleActions = _ruleActions.AsReadOnly(); } public Integration(Guid createdBy, bool status, string name) { CreatedOn = DateTime.Now.ToBrazilianTime(); CreatedBy = createdBy; Status = status; Name = name; _ruleActions = new List<RuleAction>(); RuleActions = _ruleActions.AsReadOnly(); Validate(); } } }
使用AsReadOnly()返回只读包装器,既保证外部无法修改集合,又让驱动能识别为合法的属性成员。
步骤2:完善MongoDB映射配置
在IntegrationMapping中,将RuleActions属性与私有字段_ruleActions关联,并指定集合类型的序列化器:
using ABSRotas.Common.DataAccess.DatabaseFacades.MongoDb; using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Serializers; namespace ABSRotas.Routing.Entities.Models { public class IntegrationMapping : IMongoMappings { public void Register() { BsonClassMap.RegisterClassMap<Integration>(cm => { cm.AutoMap(); // 映射私有字段到数据库的RuleActions元素 cm.MapField("_ruleActions").SetElementName("RuleActions"); // 映射公共属性到同一数据库元素,关联到私有字段 cm.MapProperty(i => i.RuleActions) .SetElementName("RuleActions") .MapToField("_ruleActions") // 指定用List<RuleAction>的序列化器处理IReadOnlyList类型 .SetSerializer(new EnumerableInterfaceImplementerSerializer<IReadOnlyList<RuleAction>, List<RuleAction>>()); cm.MapCreator(i => new Integration(i.CreatedBy, i.Status, i.Name)); }); } } }
EnumerableInterfaceImplementerSerializer用于告诉驱动,处理IReadOnlyList<RuleAction>时使用List<RuleAction>的序列化逻辑,避免类型不兼容问题。
步骤3:优化查询方法(可选但推荐)
当前Query方法会把所有数据加载到内存再转为IQueryable,存在性能问题,改为直接返回MongoDB的延迟查询IQueryable,让查询在服务器端执行:
public IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> queryExpression) where TEntity : Entity, IAggregateRoot { var database = ConnectToDatabase(); var collection = database.GetCollection<TEntity>(GetCollectionName(typeof(TEntity))); // 直接返回服务器端可执行的IQueryable return collection.AsQueryable().Where(queryExpression); }
后续LINQ操作会尽可能转为MongoDB查询语句,而非在内存中过滤数据。
验证
完成上述修改后,重新运行LINQ查询,驱动就能正确解析i.RuleActions.Count表达式,同时保持实体的封装性和查询的性能。
内容的提问来源于stack exchange,提问作者Kelvyn Sinhorini
相关产品推荐
相关产品推荐

