Skip to content

Commit f7ca6aa

Browse files
committed
CSHARP-1826: Map immutable classes correctly.
1 parent dd0f526 commit f7ca6aa

File tree

3 files changed

+62
-7
lines changed

3 files changed

+62
-7
lines changed

src/MongoDB.Bson/Serialization/Conventions/ImmutableTypeClassMapConvention.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,28 +39,45 @@ public void Apply(BsonClassMap classMap)
3939
return;
4040
}
4141

42+
var properties = typeInfo.GetProperties();
43+
if (properties.Any(p => p.CanWrite))
44+
{
45+
return; // a type that has any writable properties is not immutable
46+
}
47+
48+
var anyConstructorsWereMapped = false;
4249
foreach (var ctor in typeInfo.GetConstructors())
4350
{
4451
var parameters = ctor.GetParameters();
45-
var properties = typeInfo.GetProperties();
4652
if (parameters.Length != properties.Length)
4753
{
48-
continue;
54+
continue; // only consider constructors that have sufficient parameters to initialize all properties
4955
}
5056

5157
var matches = parameters
5258
.GroupJoin(properties,
5359
parameter => parameter.Name,
5460
property => property.Name,
55-
(parameter, props) => new { parameter, properties = props },
61+
(parameter, props) => new { Parameter = parameter, Properties = props },
5662
StringComparer.OrdinalIgnoreCase);
5763

58-
if (matches.Any(m => m.properties.Count() != 1 || m.properties.ElementAt(0).CanWrite))
64+
if (matches.Any(m => m.Properties.Count() != 1))
5965
{
6066
continue;
6167
}
6268

6369
classMap.MapConstructor(ctor);
70+
71+
anyConstructorsWereMapped = true;
72+
}
73+
74+
if (anyConstructorsWereMapped)
75+
{
76+
// if any constructors were mapped by this convention then map all the properties also
77+
foreach (var property in properties)
78+
{
79+
classMap.MapMember(property);
80+
}
6481
}
6582
}
6683
}

src/MongoDB.Bson/Serialization/Conventions/ReadWriteMemberFinderConvention.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ private void MapField(BsonClassMap classMap, FieldInfo fieldInfo)
104104

105105
private void MapProperty(BsonClassMap classMap, PropertyInfo propertyInfo)
106106
{
107-
if (!propertyInfo.CanRead || (!propertyInfo.CanWrite && classMap.ClassType.Namespace != null))
107+
if (!propertyInfo.CanRead || !propertyInfo.CanWrite)
108108
{
109-
// we can't write or it is anonymous...
109+
// only read and write properties are mapped
110110
return;
111111
}
112112

tests/MongoDB.Bson.Tests/Serialization/Conventions/ImmutableTypeClassMapConventionTests.cs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright 2010-2014 MongoDB Inc.
1+
/* Copyright 2010-2016 MongoDB Inc.
22
*
33
* Licensed under the Apache License, Version 2.0 (the "License");
44
* you may not use this file except in compliance with the License.
@@ -15,6 +15,7 @@
1515

1616
using System;
1717
using System.Linq;
18+
using FluentAssertions;
1819
using MongoDB.Bson;
1920
using MongoDB.Bson.Serialization;
2021
using MongoDB.Bson.Serialization.Conventions;
@@ -59,6 +60,43 @@ public TestClassC(string a, string b)
5960
}
6061
}
6162

63+
[Fact]
64+
public void Anonymous_class_should_map_correctly()
65+
{
66+
var c = new { A = 1, B = 2 };
67+
var classMap = BsonClassMap.LookupClassMap(c.GetType());
68+
69+
classMap.DeclaredMemberMaps.Select(m => m.MemberName).Should().Equal("A", "B");
70+
classMap.CreatorMaps.Count().Should().Be(1);
71+
}
72+
73+
[Fact]
74+
public void TestClassA_should_map_correctly()
75+
{
76+
var classMap = BsonClassMap.LookupClassMap(typeof(TestClassA));
77+
78+
classMap.DeclaredMemberMaps.Select(m => m.MemberName).Should().Equal("A", "B");
79+
classMap.CreatorMaps.Count().Should().Be(0);
80+
}
81+
82+
[Fact]
83+
public void TestClassB_should_map_correctly()
84+
{
85+
var classMap = BsonClassMap.LookupClassMap(typeof(TestClassB));
86+
87+
classMap.DeclaredMemberMaps.Select(m => m.MemberName).Should().Equal("A", "B");
88+
classMap.CreatorMaps.Count().Should().Be(1);
89+
}
90+
91+
[Fact]
92+
public void TestClassC_should_map_correctly()
93+
{
94+
var classMap = BsonClassMap.LookupClassMap(typeof(TestClassC));
95+
96+
classMap.DeclaredMemberMaps.Select(m => m.MemberName).Should().Equal("A", "B");
97+
classMap.CreatorMaps.Count().Should().Be(1);
98+
}
99+
62100
[Fact]
63101
public void TestNoDefaultConstructorClassMapConventionWithTestClassA()
64102
{

0 commit comments

Comments
 (0)