Permalink
Browse files

added default constructor to BsonElementAttribute. removed constraint…

…s related to mapping readonly fields and properties. added support for persisting readonly fields and properties and not deserializing them.
  • Loading branch information...
1 parent 8bf97a8 commit 24813c21ad9f1ae328b4f9aae9f08ce0c91a582e @craiggwilson craiggwilson committed Apr 13, 2012
View
@@ -27,3 +27,6 @@ Help
#Nupkg artifacts
*.nupkg
+
+# NCrunch artifacts
+*.ncrunch*
@@ -34,6 +34,12 @@ public class BsonElementAttribute : BsonSerializationOptionsAttribute
/// <summary>
/// Initializes a new instance of the BsonElementAttribute class.
/// </summary>
+ public BsonElementAttribute()
+ { }
+
+ /// <summary>
+ /// Initializes a new instance of the BsonElementAttribute class.
+ /// </summary>
/// <param name="elementName">The name of the element.</param>
public BsonElementAttribute(string elementName)
{
@@ -1085,7 +1085,10 @@ private BsonMemberMap AutoMapMember(MemberInfo memberInfo)
var elementAttribute = attribute as BsonElementAttribute;
if (elementAttribute != null)
{
- memberMap.SetElementName(elementAttribute.ElementName);
+ if (!string.IsNullOrEmpty(elementAttribute.ElementName))
+ {
+ memberMap.SetElementName(elementAttribute.ElementName);
+ }
memberMap.SetOrder(elementAttribute.Order);
continue;
}
@@ -1191,7 +1194,7 @@ private IEnumerable<MemberInfo> FindMembers()
foreach (var fieldInfo in _classType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
{
var elementAttribute = (BsonElementAttribute)fieldInfo.GetCustomAttributes(typeof(BsonElementAttribute), false).FirstOrDefault();
- if (elementAttribute == null || fieldInfo.IsInitOnly || fieldInfo.IsLiteral)
+ if (elementAttribute == null)
{
continue;
}
@@ -1206,7 +1209,7 @@ private IEnumerable<MemberInfo> FindMembers()
foreach (var propertyInfo in _classType.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly))
{
var elementAttribute = (BsonElementAttribute)propertyInfo.GetCustomAttributes(typeof(BsonElementAttribute), false).FirstOrDefault();
- if (elementAttribute == null || !propertyInfo.CanRead || (!propertyInfo.CanWrite && !_isAnonymous))
+ if (elementAttribute == null)
{
continue;
}
@@ -142,11 +142,15 @@ public object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializ
}
var memberMap = classMap.GetMemberMapForElement(elementName);
- if (memberMap != null && memberMap != classMap.ExtraElementsMemberMap)
+ if (memberMap != null && memberMap != classMap.ExtraElementsMemberMap && !memberMap.IsReadOnly)
{
DeserializeMember(bsonReader, obj, memberMap);
missingElementMemberMaps.Remove(memberMap);
}
+ else if (memberMap != null && memberMap.IsReadOnly)
+ {
+ bsonReader.SkipValue();
+ }
else
{
if (classMap.ExtraElementsMemberMap != null)
@@ -171,6 +175,11 @@ public object Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializ
foreach (var memberMap in missingElementMemberMaps)
{
+ if (memberMap.IsReadOnly)
+ {
+ continue;
+ }
+
if (memberMap.IsRequired)
{
var fieldOrProperty = (memberMap.MemberInfo.MemberType == MemberTypes.Field) ? "field" : "property";
@@ -240,6 +240,34 @@ public object DefaultValue
get { return _defaultValue; }
}
+ /// <summary>
+ /// Gets whether the member is readonly.
+ /// </summary>
+ /// <remarks>
+ /// Readonly indicates that the member is written to the database, but not read from the database.
+ /// </remarks>
+ public bool IsReadOnly
+ {
+ get
+ {
+ switch(_memberInfo.MemberType)
+ {
+ case MemberTypes.Field:
+ var field = (FieldInfo)_memberInfo;
+ return field.IsInitOnly || field.IsLiteral;
+ case MemberTypes.Property:
+ var property = (PropertyInfo)_memberInfo;
+ return !property.CanWrite;
+ default:
+ throw new NotSupportedException(
+ string.Format("Only fields and properties are supported by BsonMemberMap. The member {0} of class {1} is a {2}.",
+ _memberInfo.Name,
+ _memberInfo.DeclaringType.Name,
+ _memberInfo.MemberType));
+ }
+ }
+ }
+
// public methods
/// <summary>
/// Applies the default value to the member of an object.
@@ -497,10 +525,10 @@ private static object GetDefaultValue(Type type)
{
var fieldInfo = (FieldInfo)_memberInfo;
- if (fieldInfo.IsInitOnly || fieldInfo.IsLiteral)
+ if (IsReadOnly)
{
var message = string.Format(
- "The field '{0} {1}' of class '{2}' is readonly.",
+ "The field '{0} {1}' of class '{2}' is readonly. To avoid this exception, call IsReadOnly to ensure that setting a value is allowed.",
fieldInfo.FieldType.FullName, fieldInfo.Name, fieldInfo.DeclaringType.FullName);
throw new BsonSerializationException(message);
}
@@ -554,10 +582,10 @@ private static object GetDefaultValue(Type type)
{
var propertyInfo = (PropertyInfo)_memberInfo;
var setMethodInfo = propertyInfo.GetSetMethod(true);
- if (setMethodInfo == null)
+ if (IsReadOnly)
{
var message = string.Format(
- "The property '{0} {1}' of class '{2}' has no 'set' accessor.",
+ "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.",
propertyInfo.PropertyType.FullName, propertyInfo.Name, propertyInfo.DeclaringType.FullName);
throw new BsonSerializationException(message);
}
@@ -40,6 +40,8 @@ private class A
public int FieldMapped;
[BsonElement("FieldMappedByAttribute")]
private int fieldMappedByAttribute;
+ [BsonElement]
+ private readonly int fieldMappedByAttribute2;
public int PropertyMapped { get; set; }
public int PropertyMapped2 { get; private set; }
@@ -49,14 +51,25 @@ private class A
[BsonElement("PropertyMappedByAttribute")]
private int PropertyMappedByAttribute { get; set; }
+
+ [BsonElement]
+ public int PropertyMappedByAttribute2
+ {
+ get { return PropertyMapped + 1; }
+ }
+
+ public A()
+ {
+ fieldMappedByAttribute2 = 10;
+ }
}
#pragma warning restore
[Test]
public void TestMappingPicksUpAllMembersWithAttributes()
{
var classMap = BsonClassMap.LookupClassMap(typeof(A));
- Assert.AreEqual(6, classMap.AllMemberMaps.Count());
+ Assert.AreEqual(8, classMap.AllMemberMaps.Count());
}
}
@@ -75,13 +75,26 @@ static Employee()
cm.MapProperty(e => e.FirstName).SetElementName("fn");
cm.MapProperty(e => e.LastName).SetElementName("ln");
cm.MapProperty(e => e.DateOfBirth).SetElementName("dob").SetSerializer(new DateOfBirthSerializer());
+ cm.MapProperty(e => e.Age).SetElementName("age");
});
}
public ObjectId EmployeeId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
+ public int Age
+ {
+ get
+ {
+ DateTime now = DateTime.Today;
+ int age = now.Year - DateOfBirth.Year;
+ if (DateOfBirth > now.AddYears(-age))
+ age--;
+
+ return age;
+ }
+ }
}
[Test]
@@ -31,15 +31,23 @@ public class BsonMemberMapTests
{
private class TestClass
{
+ public readonly int ReadOnlyField;
+
public int Field;
public int Property { get; set; }
- public int ReadOnlyProperty { get; private set; }
+ public int PrivateSettableProperty { get; private set; }
+
+ public int ReadOnlyProperty
+ {
+ get { return Property + 1; }
+ }
public TestClass()
{
- ReadOnlyProperty = 10;
+ ReadOnlyField = 13;
+ PrivateSettableProperty = 10;
}
}
@@ -56,6 +64,15 @@ public void TestGettingAField()
}
[Test]
+ public void TestIsReadOnlyPropertyOfAField()
+ {
+ var classMap = new BsonClassMap<TestClass>(cm => cm.AutoMap());
+ var memberMap = classMap.GetMemberMap("Field");
+
+ Assert.IsFalse(memberMap.IsReadOnly);
+ }
+
+ [Test]
public void TestSettingAField()
{
var instance = new TestClass();
@@ -68,6 +85,50 @@ public void TestSettingAField()
}
[Test]
+ public void TestGettingAReadOnlyField()
+ {
+ var instance = new TestClass();
+ var classMap = new BsonClassMap<TestClass>(cm =>
+ {
+ cm.AutoMap();
+ cm.MapMember(c => c.ReadOnlyField);
+ });
+ var memberMap = classMap.GetMemberMap("ReadOnlyField");
+
+ int value = (int)memberMap.Getter(instance);
+
+ Assert.AreEqual(13, value);
+ }
+
+ [Test]
+ public void TestIsReadOnlyPropertyOfAReadOnlyField()
+ {
+ var classMap = new BsonClassMap<TestClass>(cm =>
+ {
+ cm.AutoMap();
+ cm.MapMember(c => c.ReadOnlyField);
+ });
+ var memberMap = classMap.GetMemberMap("ReadOnlyField");
+
+ Assert.IsTrue(memberMap.IsReadOnly);
+ }
+
+ [Test]
+ [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.")]
+ public void TestSettingAReadOnlyField()
+ {
+ var instance = new TestClass();
+ var classMap = new BsonClassMap<TestClass>(cm =>
+ {
+ cm.AutoMap();
+ cm.MapMember(c => c.ReadOnlyField);
+ });
+ var memberMap = classMap.GetMemberMap("ReadOnlyField");
+
+ memberMap.Setter(instance, 12);
+ }
+
+ [Test]
public void TestGettingAProperty()
{
var instance = new TestClass { Property = 42 };
@@ -80,6 +141,15 @@ public void TestGettingAProperty()
}
[Test]
+ public void TestIsReadOnlyPropertyOfAProperty()
+ {
+ var classMap = new BsonClassMap<TestClass>(cm => cm.AutoMap());
+ var memberMap = classMap.GetMemberMap("Property");
+
+ Assert.IsFalse(memberMap.IsReadOnly);
+ }
+
+ [Test]
public void TestSettingAProperty()
{
var instance = new TestClass();
@@ -92,27 +162,81 @@ public void TestSettingAProperty()
}
[Test]
- public void TestGettingAReadOnlyProperty()
+ public void TestGettingAPrivateSettableProperty()
{
var instance = new TestClass();
var classMap = new BsonClassMap<TestClass>(cm => cm.AutoMap());
- var memberMap = classMap.GetMemberMap("ReadOnlyProperty");
+ var memberMap = classMap.GetMemberMap("PrivateSettableProperty");
int value = (int)memberMap.Getter(instance);
Assert.AreEqual(10, value);
}
[Test]
- public void TestSettingAReadOnlyProperty()
+ public void TestIsReadOnlyPropertyOfAPrivateSettableProperty()
+ {
+ var classMap = new BsonClassMap<TestClass>(cm => cm.AutoMap());
+ var memberMap = classMap.GetMemberMap("PrivateSettableProperty");
+
+ Assert.IsFalse(memberMap.IsReadOnly);
+ }
+
+ [Test]
+ public void TestSettingAPrivateSettableProperty()
{
var instance = new TestClass();
var classMap = new BsonClassMap<TestClass>(cm => cm.AutoMap());
- var memberMap = classMap.GetMemberMap("ReadOnlyProperty");
+ var memberMap = classMap.GetMemberMap("PrivateSettableProperty");
memberMap.Setter(instance, 42);
- Assert.AreEqual(42, instance.ReadOnlyProperty);
+ Assert.AreEqual(42, instance.PrivateSettableProperty);
+ }
+
+ [Test]
+ public void TestGettingAReadOnlyProperty()
+ {
+ var instance = new TestClass { Property = 10 };
+ var classMap = new BsonClassMap<TestClass>(cm =>
+ {
+ cm.AutoMap();
+ cm.MapMember(c => c.ReadOnlyProperty);
+ });
+
+ var memberMap = classMap.GetMemberMap("ReadOnlyProperty");
+
+ int value = (int)memberMap.Getter(instance);
+
+ Assert.AreEqual(11, value);
+ }
+
+ [Test]
+ public void TestIsReadOnlyPropertyOfAReadOnlyProperty()
+ {
+ var classMap = new BsonClassMap<TestClass>(cm =>
+ {
+ cm.AutoMap();
+ cm.MapMember(c => c.ReadOnlyProperty);
+ });
+ var memberMap = classMap.GetMemberMap("ReadOnlyProperty");
+
+ Assert.IsTrue(memberMap.IsReadOnly);
+ }
+
+ [Test]
+ [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.")]
+ public void TestSettingAReadOnlyProperty()
+ {
+ var instance = new TestClass { Property = 10 };
+ var classMap = new BsonClassMap<TestClass>(cm =>
+ {
+ cm.AutoMap();
+ cm.MapMember(c => c.ReadOnlyProperty);
+ });
+ var memberMap = classMap.GetMemberMap("ReadOnlyProperty");
+
+ memberMap.Setter(instance, 12);
}
}
}

0 comments on commit 24813c2

Please sign in to comment.