When using the expression based query API against a property on a (nested) interface type the driver fails with an exception. The below code demonstrates the issue:
using System; using System.Linq.Expressions; using FluentAssertions; using MongoDB.Bson; using MongoDB.Bson.Serialization; using MongoDB.Driver.Linq.Translators; using Xunit; namespace MongoDB.Driver.Tests.Linq.Translators { public class InterfaceTranslatorTests { #region Failing scenario public interface IMakeTheTestFail { int I { get; set; } } public class FailingType : IMakeTheTestFail { public IMakeTheTestFail Inner { get; set; } public int I { get; set; } } [Fact] public void Fails() { Assert( (IMakeTheTestFail x) => x.I == 1, "{'I': 1}"); Assert( (FailingType x) => x.Inner.I == 1, "{'Inner.I': 1}"); } #endregion #region Working scenario public class WorkingType { public WorkingType Inner { get; set; } public int I { get; set; } } [Fact] public void Works() { Assert( (WorkingType x) => x.I == 1, "{'I': 1}"); Assert( (WorkingType x) => x.Inner.I == 1, "{'Inner.I': 1}"); } #endregion public void Assert<T>(Expression<Func<T, bool>> filter, string expectedFilter) { var serializer = BsonSerializer.SerializerRegistry.GetSerializer<T>(); var filterDocument = PredicateTranslator.Translate(filter, serializer, BsonSerializer.SerializerRegistry); filterDocument.Should().Be(BsonDocument.Parse(expectedFilter)); } } }
It all comes down to the fact that the nested property "Inner" will be automatically assigned a serializer of type DiscriminatedInterfaceSerializer which does not implement IBsonDocumentSerializer which again is fair enough since it shouldn't really know about the concretes mappings for properties on an unknown implementor of its covered interface... However, for querying purposes it would appear desirable to have that functionality in place.
The problem starts when SerializationBinder.VisitMember(MemberExpression node) performs the following type check:
var documentSerializer = serializationExpression.Serializer as IBsonDocumentSerializer; BsonSerializationInfo memberSerializationInfo; if (documentSerializer != null && documentSerializer.TryGetMemberSerializationInfo(node.Member.Name, out memberSerializationInfo))
As a result of this check, in the above example, this will cause the filter expression to be translated into "{(
{document}.I == 1)}" as opposed to "{({document}.*{I}* == 1)}" for the working case which again causes PredicateTranslator.GetFieldExpression(Expression expression) to not find a matching field and fail:
System.InvalidOperationException {document}{Inner}.I is not supported. at MongoDB.Driver.Linq.Translators.PredicateTranslator.GetFieldExpression(Expression expression) in C:\Users\daniel.hegener\Source\Repos\mongo-csharp-driver2\src\MongoDB.Driver\Linq\Translators\PredicateTranslator.cs:line 1637