使用位置运算符时,自定义反序列化器失败

时间:2019-03-28 21:20:38

标签: c# mongodb serialization

我正在为元素列表创建自定义序列化程序,这种想法是我将避免根据元素的值反序列化某些元素。

这对于大多数读取/更新调用(即

)都适用
await collection.InsertOneAsync(entry);
await collection.FindAsync(x => x.Id == entry.Id);
await collection.FindOneAndUpdateAsync(x => x.Id == entry.Id, Builders<Outer>.Update.Set(x => x.Value, 5));

但是当我使用位置运算符进行更新调用时,

var filter = Builders<Outer>.Filter.Where(x => x.Elements.Any(e => e.Id == elementId));
var update = Builders<Outer>.Update.Set(x => x.Elements[-1].Value, 5);
await collection.FindOneAndUpdateAsync(filter, update);

它失败并出现以下错误:

Unhandled Exception: System.ArgumentException: Property 'System.String Id' is not defined for type 'System.Object'
Parameter name: property
   at System.Linq.Expressions.Expression.Property(Expression expression, PropertyInfo property)
   at System.Linq.Expressions.Expression.MakeMemberAccess(Expression expression, MemberInfo member)
   at System.Linq.Expressions.MemberExpression.Update(Expression expression)
   at System.Linq.Expressions.ExpressionVisitor.VisitMember(MemberExpression node)
   at MongoDB.Driver.Linq.Processors.SerializationBinder.VisitMember(MemberExpression node)
   at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
   at MongoDB.Driver.Linq.Processors.SerializationBinder.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression node)
   at MongoDB.Driver.Linq.Processors.SerializationBinder.VisitBinary(BinaryExpression node)
   at System.Linq.Expressions.BinaryExpression.Accept(ExpressionVisitor visitor)
   at MongoDB.Driver.Linq.Processors.SerializationBinder.Visit(Expression node)
   at MongoDB.Driver.Linq.Processors.BinderHelper.BindWhere(PipelineExpression pipeline, IBindingContext bindingContext, LambdaExpression lambda)
   at MongoDB.Driver.Linq.Processors.EmbeddedPipeline.MethodCallBinders.AnyBinder.Bind(PipelineExpression pipeline, EmbeddedPipelineBindingContext bindingContext, MethodCallExpression node, IEnumerable`1 arguments)
   at MongoDB.Driver.Linq.Processors.MethodInfoMethodCallBinder`1.Bind(PipelineExpression pipeline, TBindingContext bindingContext, MethodCallExpression node, IEnumerable`1 arguments)
   at MongoDB.Driver.Linq.Processors.CompositeMethodCallBinder`1.Bind(PipelineExpression pipeline, TBindingContext bindingContext, MethodCallExpression node, IEnumerable`1 arguments)
   at MongoDB.Driver.Linq.Processors.PipelineBinderBase`1.BindMethodCall(MethodCallExpression node)
   at MongoDB.Driver.Linq.Processors.EmbeddedPipeline.EmbeddedPipelineBinder.Bind(Expression node, IBindingContext parent)
   at MongoDB.Driver.Linq.Processors.SerializationBinder.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at MongoDB.Driver.Linq.Processors.SerializationBinder.Visit(Expression node)
   at MongoDB.Driver.Linq.Translators.PredicateTranslator.Translate[TDocument](Expression`1 predicate, IBsonSerializer`1 parameterSerializer, IBsonSerializerRegistry serializerRegistry)
   at MongoDB.Driver.MongoCollectionImpl`1.CreateFindOneAndUpdateOperation[TProjection](FilterDefinition`1 filter, UpdateDefinition`1 update, FindOneAndUpdateOptions`2 options)
   at MongoDB.Driver.MongoCollectionImpl`1.FindOneAndUpdateAsync[TProjection](IClientSessionHandle session, FilterDefinition`1 filter, UpdateDefinition`1 update, FindOneAndUpdateOptions`2 options, CancellationToken cancellationToken)
   at MongoDB.Driver.MongoCollectionImpl`1.<>c__DisplayClass55_0`1.<FindOneAndUpdateAsync>b__0(IClientSessionHandle session)
   at MongoDB.Driver.MongoCollectionImpl`1.UsingImplicitSessionAsync[TResult](Func`2 funcAsync, CancellationToken cancellationToken)
   at MongoFiltering.Program.Main(String[] args) in C:\Users\jeroen.vannevel\source\repos\MongoFiltering\MongoFiltering\Program.cs:line 51
   at MongoFiltering.Program.<Main>(String[] args)

我将其缩小为以下示例。据我所知,您的自定义反序列化程序实际上不需要执行任何工作,只是它的存在会破坏过滤子句。

下面是我使用的模型和空的自定义序列化程序。我创建了一个MCVE here,它使用Mongo2Go构建了一个动态的mongo守护程序-您可以执行它。

public class Outer
    {
        public string Id { get; set; }
        public int Value { get; set; }

        [BsonSerializer(typeof(ElementsSerializer))]
        public List<Element> Elements { get; set; }
    }

    public class Element
    {
        public string Id { get; set; }
        public int Value { get; set; }
    }

    public class ElementsSerializer : EnumerableSerializerBase<List<Element>>
    {
        public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, List<Element> value) => base.Serialize(context, args, value);

        public override List<Element> Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => base.Deserialize(context, args);

        protected override void AddItem(object accumulator, object item) => ((List<Element>)accumulator).Add((Element)item);
        protected override object CreateAccumulator() => new List<Element>();
        protected override IEnumerable EnumerateItemsInSerializationOrder(List<Element> value) => value;
        protected override List<Element> FinalizeResult(object accumulator) => (List<Element>)accumulator;
    }

1 个答案:

答案 0 :(得分:1)

我发现了问题。似乎有两个基类可以继承:override func viewDidLoad() { super.viewDidLoad() // configure your views and subviews here } EnumerableSerializerBase<TValue>。 如果您使用前者(我这样做了),它将使用a simple object deserializer。但是,使用另一个重载uses the generic deserializer

最终的解串器如下所示,并且可以按预期工作:

EnumerableSerializerBase<TValue, TItem>

否则,public class ElementsSerializer : EnumerableSerializerBase<List<Element>, Element> { public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, List<Element> value) => base.Serialize(context, args, value); public override List<Element> Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) => base.Deserialize(context, args); protected override object CreateAccumulator() => new List<Element>(); protected override List<Element> FinalizeResult(object accumulator) => (List<Element>)accumulator; protected override void AddItem(object accumulator, Element item) => ((List<Element>)accumulator).Add(item); protected override IEnumerable<Element> EnumerateItemsInSerializationOrder(List<Element> value) => value; } will register的表达式将被我们的lambda值替换为上述序列化器的结果。也就是,我们将EmbeddedPipelineBinder lambda值的所有用法替换为Element类型的文档。然后,当我们的Lambda尝试访问object属性时,它抱怨说类型.Id中没有该属性,这是正确的。

我认为,仅当尝试访问嵌套集合时才会显示此原因的原因是因为初始级别uses the PipelineBinder(请注意缺少 Embedded )没有此功能特殊的数组反序列化逻辑。