Skip to content

Commit 16f12a9

Browse files
committed
CSHARP:1253: Fixed issue with projecting into a derived class.
Conflicts: src/MongoDB.Driver/Linq/Processors/SerializerBuilder.cs
1 parent ebee7b1 commit 16f12a9

File tree

5 files changed

+75
-23
lines changed

5 files changed

+75
-23
lines changed

src/MongoDB.Bson/Serialization/BsonClassMap.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace MongoDB.Bson.Serialization
2929
/// <summary>
3030
/// Represents a mapping between a class and a BSON document.
3131
/// </summary>
32-
public abstract class BsonClassMap
32+
public class BsonClassMap
3333
{
3434
// private static fields
3535
private readonly static Dictionary<Type, BsonClassMap> __classMaps = new Dictionary<Type, BsonClassMap>();
@@ -67,7 +67,7 @@ public abstract class BsonClassMap
6767
/// Initializes a new instance of the BsonClassMap class.
6868
/// </summary>
6969
/// <param name="classType">The class type.</param>
70-
protected BsonClassMap(Type classType)
70+
public BsonClassMap(Type classType)
7171
{
7272
_classType = classType;
7373
_creatorMaps = new List<BsonCreatorMap>();
@@ -81,6 +81,17 @@ protected BsonClassMap(Type classType)
8181
Reset();
8282
}
8383

84+
/// <summary>
85+
/// Initializes a new instance of the <see cref="BsonClassMap"/> class.
86+
/// </summary>
87+
/// <param name="classType">Type of the class.</param>
88+
/// <param name="baseClassMap">The base class map.</param>
89+
public BsonClassMap(Type classType, BsonClassMap baseClassMap)
90+
: this(classType)
91+
{
92+
_baseClassMap = baseClassMap;
93+
}
94+
8495
// public properties
8596
/// <summary>
8697
/// Gets all the member maps (including maps for inherited members).
@@ -448,7 +459,10 @@ public BsonClassMap Freeze()
448459
var baseType = _classType.BaseType;
449460
if (baseType != null)
450461
{
451-
_baseClassMap = LookupClassMap(baseType);
462+
if (_baseClassMap == null)
463+
{
464+
_baseClassMap = LookupClassMap(baseType);
465+
}
452466
_discriminatorIsRequired |= _baseClassMap._discriminatorIsRequired;
453467
_hasRootClass |= (_isRootClass || _baseClassMap.HasRootClass);
454468
_allMemberMaps.AddRange(_baseClassMap.AllMemberMaps);

src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,24 @@ namespace MongoDB.Bson.Serialization
3030
public class BsonClassMapSerializer<TClass> : SerializerBase<TClass>, IBsonIdProvider, IBsonDocumentSerializer, IBsonPolymorphicSerializer
3131
{
3232
// private fields
33-
private BsonClassMap<TClass> _classMap;
33+
private BsonClassMap _classMap;
3434

3535
// constructors
3636
/// <summary>
3737
/// Initializes a new instance of the BsonClassMapSerializer class.
3838
/// </summary>
3939
/// <param name="classMap">The class map.</param>
40-
public BsonClassMapSerializer(BsonClassMap<TClass> classMap)
40+
public BsonClassMapSerializer(BsonClassMap classMap)
4141
{
4242
if (classMap == null)
4343
{
4444
throw new ArgumentNullException("classMap");
4545
}
46+
if (classMap.ClassType != typeof(TClass))
47+
{
48+
var message = string.Format("Must be a BsonClassMap for the type {0}.", typeof(TClass));
49+
throw new ArgumentException(message, "classMap");
50+
}
4651

4752
_classMap = classMap;
4853
}
@@ -128,7 +133,7 @@ public TClass DeserializeClass(BsonDeserializationContext context)
128133
else
129134
{
130135
// for mutable classes we deserialize the values directly into the result object
131-
document = _classMap.CreateInstance();
136+
document = (TClass)_classMap.CreateInstance();
132137

133138
supportsInitialization = document as ISupportInitialize;
134139
if (supportsInitialization != null)

src/MongoDB.Driver.Tests/Linq/Translators/AggregateProjectionTranslatorTests_Project.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,17 @@ public async Task Should_translate_array_projection()
744744
result.Value.Result.Should().BeEquivalentTo(33, 55);
745745
}
746746

747+
[Test]
748+
public async Task Should_translate_a_derived_class_projection()
749+
{
750+
var result = await Project(x => new DerivedRootView { Property = x.A, DerivedProperty = x.B });
751+
752+
result.Projection.Should().Be("{ Property: \"$A\", DerivedProperty: \"$B\", _id: 0 }");
753+
754+
result.Value.Property.Should().Be("Awesome");
755+
result.Value.DerivedProperty.Should().Be("Balloon");
756+
}
757+
747758
[Test]
748759
[Ignore("MongoDB does something weird with this result. It returns F and H as two separate arrays, not an array of documents")]
749760
public async Task Should_translate_array_projection_complex()

src/MongoDB.Driver.Tests/Linq/Translators/TranslatorTestBase.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ public RootView(string property)
9393
public string Field = null;
9494
}
9595

96+
public class DerivedRootView : RootView
97+
{
98+
public string DerivedProperty { get; set; }
99+
}
100+
96101
public class Root : IRoot
97102
{
98103
public int Id { get; set; }

src/MongoDB.Driver/Linq/Translators/AggregateProjectionTranslator.cs

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -658,9 +658,39 @@ private IBsonSerializer BuildProjectedSerializer(ProjectionMapping mapping)
658658
// the same anonymous type definition in different contexts as long as they
659659
// are structurally equatable. As such, it might be that two different queries
660660
// projecting the same shape might need to be deserialized differently.
661-
var classMapType = typeof(BsonClassMap<>).MakeGenericType(mapping.Expression.Type);
662-
BsonClassMap classMap = (BsonClassMap)Activator.CreateInstance(classMapType);
663-
foreach (var memberMapping in mapping.Members)
661+
var classMap = BuildClassMap(mapping.Expression.Type, mapping);
662+
663+
var mappedParameters = mapping.Members
664+
.Where(x => x.Parameter != null)
665+
.OrderBy(x => x.Parameter.Position)
666+
.Select(x => x.Member)
667+
.ToList();
668+
669+
if (mappedParameters.Count > 0)
670+
{
671+
classMap.MapConstructor(mapping.Constructor)
672+
.SetArguments(mappedParameters);
673+
}
674+
675+
var serializerType = typeof(BsonClassMapSerializer<>).MakeGenericType(mapping.Expression.Type);
676+
return (IBsonSerializer)Activator.CreateInstance(serializerType, classMap.Freeze());
677+
}
678+
679+
private BsonClassMap BuildClassMap(Type type, ProjectionMapping mapping)
680+
{
681+
if (type == null || type == typeof(object))
682+
{
683+
return null;
684+
}
685+
686+
var baseClassMap = BuildClassMap(type.BaseType, mapping);
687+
if (baseClassMap != null)
688+
{
689+
baseClassMap.Freeze();
690+
}
691+
var classMap = new BsonClassMap(type, baseClassMap);
692+
693+
foreach (var memberMapping in mapping.Members.Where(x => x.Member.DeclaringType == type))
664694
{
665695
var serializationExpression = memberMapping.Expression as ISerializationExpression;
666696
if (serializationExpression == null)
@@ -685,20 +715,7 @@ private IBsonSerializer BuildProjectedSerializer(ProjectionMapping mapping)
685715
}
686716
}
687717

688-
var mappedParameters = mapping.Members
689-
.Where(x => x.Parameter != null)
690-
.OrderBy(x => x.Parameter.Position)
691-
.Select(x => x.Member)
692-
.ToList();
693-
694-
if (mappedParameters.Count > 0)
695-
{
696-
classMap.MapConstructor(mapping.Constructor)
697-
.SetArguments(mappedParameters);
698-
}
699-
700-
var serializerType = typeof(BsonClassMapSerializer<>).MakeGenericType(mapping.Expression.Type);
701-
return (IBsonSerializer)Activator.CreateInstance(serializerType, classMap.Freeze());
718+
return classMap;
702719
}
703720
}
704721

0 commit comments

Comments
 (0)