Skip to content

Commit be41c1c

Browse files
author
rstam
committed
Merge branch 'csharp_441' of git://github.com/craiggwilson/mongo-csharp-driver into csharp441
2 parents ca00451 + 24813c2 commit be41c1c

File tree

8 files changed

+215
-16
lines changed

8 files changed

+215
-16
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ Help
2727

2828
#Nupkg artifacts
2929
*.nupkg
30+
31+
# NCrunch artifacts
32+
*.ncrunch*

Bson/Serialization/Attributes/BsonElementAttribute.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ public class BsonElementAttribute : BsonSerializationOptionsAttribute
3131
private int _order = int.MaxValue;
3232

3333
// constructors
34+
/// <summary>
35+
/// Initializes a new instance of the BsonElementAttribute class.
36+
/// </summary>
37+
public BsonElementAttribute()
38+
{ }
39+
3440
/// <summary>
3541
/// Initializes a new instance of the BsonElementAttribute class.
3642
/// </summary>

Bson/Serialization/BsonClassMap.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,10 @@ private BsonMemberMap AutoMapMember(MemberInfo memberInfo)
10851085
var elementAttribute = attribute as BsonElementAttribute;
10861086
if (elementAttribute != null)
10871087
{
1088-
memberMap.SetElementName(elementAttribute.ElementName);
1088+
if (!string.IsNullOrEmpty(elementAttribute.ElementName))
1089+
{
1090+
memberMap.SetElementName(elementAttribute.ElementName);
1091+
}
10891092
memberMap.SetOrder(elementAttribute.Order);
10901093
continue;
10911094
}
@@ -1191,7 +1194,7 @@ private IEnumerable<MemberInfo> FindMembers()
11911194
foreach (var fieldInfo in _classType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
11921195
{
11931196
var elementAttribute = (BsonElementAttribute)fieldInfo.GetCustomAttributes(typeof(BsonElementAttribute), false).FirstOrDefault();
1194-
if (elementAttribute == null || fieldInfo.IsInitOnly || fieldInfo.IsLiteral)
1197+
if (elementAttribute == null)
11951198
{
11961199
continue;
11971200
}
@@ -1206,7 +1209,7 @@ private IEnumerable<MemberInfo> FindMembers()
12061209
foreach (var propertyInfo in _classType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
12071210
{
12081211
var elementAttribute = (BsonElementAttribute)propertyInfo.GetCustomAttributes(typeof(BsonElementAttribute), false).FirstOrDefault();
1209-
if (elementAttribute == null || !propertyInfo.CanRead || (!propertyInfo.CanWrite && !_isAnonymous))
1212+
if (elementAttribute == null)
12101213
{
12111214
continue;
12121215
}

Bson/Serialization/BsonClassMapSerializer.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,15 @@ public object Deserialize(
142142
}
143143

144144
var memberMap = classMap.GetMemberMapForElement(elementName);
145-
if (memberMap != null && memberMap != classMap.ExtraElementsMemberMap)
145+
if (memberMap != null && memberMap != classMap.ExtraElementsMemberMap && !memberMap.IsReadOnly)
146146
{
147147
DeserializeMember(bsonReader, obj, memberMap);
148148
missingElementMemberMaps.Remove(memberMap);
149149
}
150+
else if (memberMap != null && memberMap.IsReadOnly)
151+
{
152+
bsonReader.SkipValue();
153+
}
150154
else
151155
{
152156
if (classMap.ExtraElementsMemberMap != null)
@@ -171,6 +175,11 @@ public object Deserialize(
171175

172176
foreach (var memberMap in missingElementMemberMaps)
173177
{
178+
if (memberMap.IsReadOnly)
179+
{
180+
continue;
181+
}
182+
174183
if (memberMap.IsRequired)
175184
{
176185
var fieldOrProperty = (memberMap.MemberInfo.MemberType == MemberTypes.Field) ? "field" : "property";

Bson/Serialization/BsonMemberMap.cs

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,34 @@ public object DefaultValue
240240
get { return _defaultValue; }
241241
}
242242

243+
/// <summary>
244+
/// Gets whether the member is readonly.
245+
/// </summary>
246+
/// <remarks>
247+
/// Readonly indicates that the member is written to the database, but not read from the database.
248+
/// </remarks>
249+
public bool IsReadOnly
250+
{
251+
get
252+
{
253+
switch(_memberInfo.MemberType)
254+
{
255+
case MemberTypes.Field:
256+
var field = (FieldInfo)_memberInfo;
257+
return field.IsInitOnly || field.IsLiteral;
258+
case MemberTypes.Property:
259+
var property = (PropertyInfo)_memberInfo;
260+
return !property.CanWrite;
261+
default:
262+
throw new NotSupportedException(
263+
string.Format("Only fields and properties are supported by BsonMemberMap. The member {0} of class {1} is a {2}.",
264+
_memberInfo.Name,
265+
_memberInfo.DeclaringType.Name,
266+
_memberInfo.MemberType));
267+
}
268+
}
269+
}
270+
243271
// public methods
244272
/// <summary>
245273
/// Applies the default value to the member of an object.
@@ -497,10 +525,10 @@ private Action<object, object> GetFieldSetter()
497525
{
498526
var fieldInfo = (FieldInfo)_memberInfo;
499527

500-
if (fieldInfo.IsInitOnly || fieldInfo.IsLiteral)
528+
if (IsReadOnly)
501529
{
502530
var message = string.Format(
503-
"The field '{0} {1}' of class '{2}' is readonly.",
531+
"The field '{0} {1}' of class '{2}' is readonly. To avoid this exception, call IsReadOnly to ensure that setting a value is allowed.",
504532
fieldInfo.FieldType.FullName, fieldInfo.Name, fieldInfo.DeclaringType.FullName);
505533
throw new BsonSerializationException(message);
506534
}
@@ -554,10 +582,10 @@ private Action<object, object> GetPropertySetter()
554582
{
555583
var propertyInfo = (PropertyInfo)_memberInfo;
556584
var setMethodInfo = propertyInfo.GetSetMethod(true);
557-
if (setMethodInfo == null)
585+
if (IsReadOnly)
558586
{
559587
var message = string.Format(
560-
"The property '{0} {1}' of class '{2}' has no 'set' accessor.",
588+
"The property '{0} {1}' of class '{2}' has no 'set' accessor. To avoid this exception, call IsReadOnly to ensure that setting a value is allowed.",
561589
propertyInfo.PropertyType.FullName, propertyInfo.Name, propertyInfo.DeclaringType.FullName);
562590
throw new BsonSerializationException(message);
563591
}

BsonUnitTests/DefaultSerializer/BsonClassMapTests.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ private class A
4040
public int FieldMapped;
4141
[BsonElement("FieldMappedByAttribute")]
4242
private int fieldMappedByAttribute;
43+
[BsonElement]
44+
private readonly int fieldMappedByAttribute2;
4345

4446
public int PropertyMapped { get; set; }
4547
public int PropertyMapped2 { get; private set; }
@@ -49,14 +51,25 @@ private class A
4951

5052
[BsonElement("PropertyMappedByAttribute")]
5153
private int PropertyMappedByAttribute { get; set; }
54+
55+
[BsonElement]
56+
public int PropertyMappedByAttribute2
57+
{
58+
get { return PropertyMapped + 1; }
59+
}
60+
61+
public A()
62+
{
63+
fieldMappedByAttribute2 = 10;
64+
}
5265
}
5366
#pragma warning restore
5467

5568
[Test]
5669
public void TestMappingPicksUpAllMembersWithAttributes()
5770
{
5871
var classMap = BsonClassMap.LookupClassMap(typeof(A));
59-
Assert.AreEqual(6, classMap.AllMemberMaps.Count());
72+
Assert.AreEqual(8, classMap.AllMemberMaps.Count());
6073
}
6174
}
6275

BsonUnitTests/DefaultSerializer/BsonDefaultSerializerTests.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,26 @@ static Employee()
7575
cm.MapProperty(e => e.FirstName).SetElementName("fn");
7676
cm.MapProperty(e => e.LastName).SetElementName("ln");
7777
cm.MapProperty(e => e.DateOfBirth).SetElementName("dob").SetSerializer(new DateOfBirthSerializer());
78+
cm.MapProperty(e => e.Age).SetElementName("age");
7879
});
7980
}
8081

8182
public ObjectId EmployeeId { get; set; }
8283
public string FirstName { get; set; }
8384
public string LastName { get; set; }
8485
public DateTime DateOfBirth { get; set; }
86+
public int Age
87+
{
88+
get
89+
{
90+
DateTime now = DateTime.Today;
91+
int age = now.Year - DateOfBirth.Year;
92+
if (DateOfBirth > now.AddYears(-age))
93+
age--;
94+
95+
return age;
96+
}
97+
}
8598
}
8699

87100
[Test]

BsonUnitTests/DefaultSerializer/BsonMemberMapTests.cs

Lines changed: 131 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,23 @@ public class BsonMemberMapTests
3131
{
3232
private class TestClass
3333
{
34+
public readonly int ReadOnlyField;
35+
3436
public int Field;
3537

3638
public int Property { get; set; }
3739

38-
public int ReadOnlyProperty { get; private set; }
40+
public int PrivateSettableProperty { get; private set; }
41+
42+
public int ReadOnlyProperty
43+
{
44+
get { return Property + 1; }
45+
}
3946

4047
public TestClass()
4148
{
42-
ReadOnlyProperty = 10;
49+
ReadOnlyField = 13;
50+
PrivateSettableProperty = 10;
4351
}
4452
}
4553

@@ -55,6 +63,15 @@ public void TestGettingAField()
5563
Assert.AreEqual(42, value);
5664
}
5765

66+
[Test]
67+
public void TestIsReadOnlyPropertyOfAField()
68+
{
69+
var classMap = new BsonClassMap<TestClass>(cm => cm.AutoMap());
70+
var memberMap = classMap.GetMemberMap("Field");
71+
72+
Assert.IsFalse(memberMap.IsReadOnly);
73+
}
74+
5875
[Test]
5976
public void TestSettingAField()
6077
{
@@ -67,6 +84,50 @@ public void TestSettingAField()
6784
Assert.AreEqual(42, instance.Field);
6885
}
6986

87+
[Test]
88+
public void TestGettingAReadOnlyField()
89+
{
90+
var instance = new TestClass();
91+
var classMap = new BsonClassMap<TestClass>(cm =>
92+
{
93+
cm.AutoMap();
94+
cm.MapMember(c => c.ReadOnlyField);
95+
});
96+
var memberMap = classMap.GetMemberMap("ReadOnlyField");
97+
98+
int value = (int)memberMap.Getter(instance);
99+
100+
Assert.AreEqual(13, value);
101+
}
102+
103+
[Test]
104+
public void TestIsReadOnlyPropertyOfAReadOnlyField()
105+
{
106+
var classMap = new BsonClassMap<TestClass>(cm =>
107+
{
108+
cm.AutoMap();
109+
cm.MapMember(c => c.ReadOnlyField);
110+
});
111+
var memberMap = classMap.GetMemberMap("ReadOnlyField");
112+
113+
Assert.IsTrue(memberMap.IsReadOnly);
114+
}
115+
116+
[Test]
117+
[ExpectedException(typeof(BsonSerializationException), ExpectedMessage = "The field 'System.Int32 ReadOnlyField' of class 'MongoDB.BsonUnitTests.Serialization.BsonMemberMapTests+TestClass' is readonly. To avoid this exception, call IsReadOnly to ensure that setting a value is allowed.")]
118+
public void TestSettingAReadOnlyField()
119+
{
120+
var instance = new TestClass();
121+
var classMap = new BsonClassMap<TestClass>(cm =>
122+
{
123+
cm.AutoMap();
124+
cm.MapMember(c => c.ReadOnlyField);
125+
});
126+
var memberMap = classMap.GetMemberMap("ReadOnlyField");
127+
128+
memberMap.Setter(instance, 12);
129+
}
130+
70131
[Test]
71132
public void TestGettingAProperty()
72133
{
@@ -79,6 +140,15 @@ public void TestGettingAProperty()
79140
Assert.AreEqual(42, value);
80141
}
81142

143+
[Test]
144+
public void TestIsReadOnlyPropertyOfAProperty()
145+
{
146+
var classMap = new BsonClassMap<TestClass>(cm => cm.AutoMap());
147+
var memberMap = classMap.GetMemberMap("Property");
148+
149+
Assert.IsFalse(memberMap.IsReadOnly);
150+
}
151+
82152
[Test]
83153
public void TestSettingAProperty()
84154
{
@@ -92,27 +162,81 @@ public void TestSettingAProperty()
92162
}
93163

94164
[Test]
95-
public void TestGettingAReadOnlyProperty()
165+
public void TestGettingAPrivateSettableProperty()
96166
{
97167
var instance = new TestClass();
98168
var classMap = new BsonClassMap<TestClass>(cm => cm.AutoMap());
99-
var memberMap = classMap.GetMemberMap("ReadOnlyProperty");
169+
var memberMap = classMap.GetMemberMap("PrivateSettableProperty");
100170

101171
int value = (int)memberMap.Getter(instance);
102172

103173
Assert.AreEqual(10, value);
104174
}
105175

106176
[Test]
107-
public void TestSettingAReadOnlyProperty()
177+
public void TestIsReadOnlyPropertyOfAPrivateSettableProperty()
178+
{
179+
var classMap = new BsonClassMap<TestClass>(cm => cm.AutoMap());
180+
var memberMap = classMap.GetMemberMap("PrivateSettableProperty");
181+
182+
Assert.IsFalse(memberMap.IsReadOnly);
183+
}
184+
185+
[Test]
186+
public void TestSettingAPrivateSettableProperty()
108187
{
109188
var instance = new TestClass();
110189
var classMap = new BsonClassMap<TestClass>(cm => cm.AutoMap());
111-
var memberMap = classMap.GetMemberMap("ReadOnlyProperty");
190+
var memberMap = classMap.GetMemberMap("PrivateSettableProperty");
112191

113192
memberMap.Setter(instance, 42);
114193

115-
Assert.AreEqual(42, instance.ReadOnlyProperty);
194+
Assert.AreEqual(42, instance.PrivateSettableProperty);
195+
}
196+
197+
[Test]
198+
public void TestGettingAReadOnlyProperty()
199+
{
200+
var instance = new TestClass { Property = 10 };
201+
var classMap = new BsonClassMap<TestClass>(cm =>
202+
{
203+
cm.AutoMap();
204+
cm.MapMember(c => c.ReadOnlyProperty);
205+
});
206+
207+
var memberMap = classMap.GetMemberMap("ReadOnlyProperty");
208+
209+
int value = (int)memberMap.Getter(instance);
210+
211+
Assert.AreEqual(11, value);
212+
}
213+
214+
[Test]
215+
public void TestIsReadOnlyPropertyOfAReadOnlyProperty()
216+
{
217+
var classMap = new BsonClassMap<TestClass>(cm =>
218+
{
219+
cm.AutoMap();
220+
cm.MapMember(c => c.ReadOnlyProperty);
221+
});
222+
var memberMap = classMap.GetMemberMap("ReadOnlyProperty");
223+
224+
Assert.IsTrue(memberMap.IsReadOnly);
225+
}
226+
227+
[Test]
228+
[ExpectedException(typeof(BsonSerializationException), ExpectedMessage = "The property 'System.Int32 ReadOnlyProperty' of class 'MongoDB.BsonUnitTests.Serialization.BsonMemberMapTests+TestClass' has no 'set' accessor. To avoid this exception, call IsReadOnly to ensure that setting a value is allowed.")]
229+
public void TestSettingAReadOnlyProperty()
230+
{
231+
var instance = new TestClass { Property = 10 };
232+
var classMap = new BsonClassMap<TestClass>(cm =>
233+
{
234+
cm.AutoMap();
235+
cm.MapMember(c => c.ReadOnlyProperty);
236+
});
237+
var memberMap = classMap.GetMemberMap("ReadOnlyProperty");
238+
239+
memberMap.Setter(instance, 12);
116240
}
117241
}
118242
}

0 commit comments

Comments
 (0)