Skip to content

Commit 4bf6280

Browse files
committed
CSHARP-1839: EnumRepresentationConvention should apply to nullable enums.
1 parent a417730 commit 4bf6280

File tree

2 files changed

+118
-56
lines changed

2 files changed

+118
-56
lines changed

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

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,28 +30,29 @@ public class EnumRepresentationConvention : ConventionBase, IMemberMapConvention
3030
// constructors
3131
/// <summary>
3232
/// Initializes a new instance of the <see cref="EnumRepresentationConvention" /> class.
33-
/// </summary>
33+
/// </summary>
3434
/// <param name="representation">The serialization representation. 0 is used to detect representation
3535
/// from the enum itself.</param>
3636
public EnumRepresentationConvention(BsonType representation)
3737
{
38-
if (!((representation == 0) ||
39-
(representation == BsonType.String) ||
40-
(representation == BsonType.Int32) ||
41-
(representation == BsonType.Int64)))
42-
{
43-
throw new ArgumentException("Enums can only be represented as String, Int32, Int64 or the type of the enum");
44-
}
38+
EnsureRepresentationIsValidForEnums(representation);
4539
_representation = representation;
4640
}
4741

42+
/// <summary>
43+
/// Gets the representation.
44+
/// </summary>
45+
public BsonType Representation => _representation;
46+
4847
/// <summary>
4948
/// Applies a modification to the member map.
5049
/// </summary>
5150
/// <param name="memberMap">The member map.</param>
5251
public void Apply(BsonMemberMap memberMap)
5352
{
54-
var memberTypeInfo = memberMap.MemberType.GetTypeInfo();
53+
var memberType = memberMap.MemberType;
54+
var memberTypeInfo = memberType.GetTypeInfo();
55+
5556
if (memberTypeInfo.IsEnum)
5657
{
5758
var serializer = memberMap.GetSerializer();
@@ -61,7 +62,48 @@ public void Apply(BsonMemberMap memberMap)
6162
var reconfiguredSerializer = representationConfigurableSerializer.WithRepresentation(_representation);
6263
memberMap.SetSerializer(reconfiguredSerializer);
6364
}
65+
return;
66+
}
67+
68+
if (IsNullableEnum(memberType))
69+
{
70+
var serializer = memberMap.GetSerializer();
71+
var childSerializerConfigurableSerializer = serializer as IChildSerializerConfigurable;
72+
if (childSerializerConfigurableSerializer != null)
73+
{
74+
var childSerializer = childSerializerConfigurableSerializer.ChildSerializer;
75+
var representationConfigurableChildSerializer = childSerializer as IRepresentationConfigurable;
76+
if (representationConfigurableChildSerializer != null)
77+
{
78+
var reconfiguredChildSerializer = representationConfigurableChildSerializer.WithRepresentation(_representation);
79+
var reconfiguredSerializer = childSerializerConfigurableSerializer.WithChildSerializer(reconfiguredChildSerializer);
80+
memberMap.SetSerializer(reconfiguredSerializer);
81+
}
82+
}
83+
return;
84+
}
85+
}
86+
87+
// private methods
88+
private bool IsNullableEnum(Type type)
89+
{
90+
return
91+
type.GetTypeInfo().IsGenericType &&
92+
type.GetGenericTypeDefinition() == typeof(Nullable<>) &&
93+
Nullable.GetUnderlyingType(type).GetTypeInfo().IsEnum;
94+
}
95+
96+
private void EnsureRepresentationIsValidForEnums(BsonType representation)
97+
{
98+
if (
99+
representation == 0 ||
100+
representation == BsonType.String ||
101+
representation == BsonType.Int32 ||
102+
representation == BsonType.Int64)
103+
{
104+
return;
64105
}
106+
throw new ArgumentException("Enums can only be represented as String, Int32, Int64 or the type of the enum", "representation");
65107
}
66108
}
67109
}
Lines changed: 67 additions & 47 deletions
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.
@@ -14,76 +14,96 @@
1414
*/
1515

1616
using System;
17-
18-
using MongoDB.Bson;
17+
using System.Linq.Expressions;
18+
using FluentAssertions;
1919
using MongoDB.Bson.Serialization;
2020
using MongoDB.Bson.Serialization.Conventions;
21-
using MongoDB.Bson.Serialization.Options;
21+
using MongoDB.Bson.Serialization.Serializers;
2222
using Xunit;
2323

2424
namespace MongoDB.Bson.Tests.Serialization.Conventions
2525
{
2626
public class EnumRepresentationConventionTests
2727
{
28-
private enum WorkDays {
29-
Monday,
30-
Wednesday,
31-
Friday
32-
};
28+
public enum E { A, B };
3329

34-
private class TestClass
30+
public class C
3531
{
36-
public string NonEnum { get; set; }
37-
public WorkDays DefaultEnum { get; set; }
38-
public WorkDays ChangedRepresentationEnum { get; set; }
32+
public E E { get; set; }
33+
public E? NE { get; set; }
34+
public int I { get; set; }
35+
}
36+
37+
[Theory]
38+
[InlineData(BsonType.Int32)]
39+
[InlineData(BsonType.Int64)]
40+
public void Apply_should_configure_serializer_when_member_is_an_enum(BsonType representation)
41+
{
42+
var subject = new EnumRepresentationConvention(representation);
43+
var memberMap = CreateMemberMap(c => c.E);
44+
45+
subject.Apply(memberMap);
46+
47+
var serializer = (EnumSerializer<E>)memberMap.GetSerializer();
48+
serializer.Representation.Should().Be(representation);
49+
}
50+
51+
52+
[Theory]
53+
[InlineData(BsonType.Int32)]
54+
[InlineData(BsonType.Int64)]
55+
public void Apply_should_configure_serializer_when_member_is_a_nullable_enum(BsonType representation)
56+
{
57+
var subject = new EnumRepresentationConvention(representation);
58+
var memberMap = CreateMemberMap(c => c.NE);
59+
60+
subject.Apply(memberMap);
61+
62+
var serializer = (IChildSerializerConfigurable)memberMap.GetSerializer();
63+
var childSerializer = (EnumSerializer<E>)serializer.ChildSerializer;
64+
childSerializer.Representation.Should().Be(representation);
65+
}
66+
67+
[Fact]
68+
public void Apply_should_do_nothing_when_member_is_not_an_enum()
69+
{
70+
var subject = new EnumRepresentationConvention(BsonType.String);
71+
var memberMap = CreateMemberMap(c => c.I);
72+
var serializer = memberMap.GetSerializer();
73+
74+
subject.Apply(memberMap);
75+
76+
memberMap.GetSerializer().Should().BeSameAs(serializer);
3977
}
4078

4179
[Theory]
4280
[InlineData(0)]
4381
[InlineData(BsonType.Int32)]
4482
[InlineData(BsonType.Int64)]
4583
[InlineData(BsonType.String)]
46-
public void TestConvention(BsonType value)
84+
public void constructor_should_initialize_instance_when_representation_is_valid(BsonType representation)
4785
{
48-
var convention = new EnumRepresentationConvention(value);
49-
var classMap = new BsonClassMap<TestClass>();
50-
var nonEnumMemberMap = classMap.MapMember(x => x.NonEnum);
51-
classMap.MapMember(x => x.DefaultEnum);
52-
var changedEnumMemberMap = classMap.MapMember(x => x.ChangedRepresentationEnum);
53-
convention.Apply(nonEnumMemberMap);
54-
convention.Apply(changedEnumMemberMap);
55-
Assert.Equal(value, ((IRepresentationConfigurable)(changedEnumMemberMap.GetSerializer())).Representation);
86+
var subject = new EnumRepresentationConvention(representation);
87+
88+
subject.Representation.Should().Be(representation);
5689
}
5790

58-
[Fact]
59-
public void TestConventionOverride()
91+
[Theory]
92+
[InlineData(BsonType.Decimal128)]
93+
[InlineData(BsonType.Double)]
94+
public void constructor_should_throw_when_representation_is_not_valid(BsonType representation)
6095
{
61-
var int64Convention = new EnumRepresentationConvention(BsonType.Int64);
62-
var strConvention = new EnumRepresentationConvention(BsonType.String);
63-
var classMap = new BsonClassMap<TestClass>();
64-
var memberMap = classMap.MapMember(x => x.ChangedRepresentationEnum);
65-
int64Convention.Apply(memberMap);
66-
strConvention.Apply(memberMap);
67-
Assert.Equal(BsonType.String, ((IRepresentationConfigurable)(memberMap.GetSerializer())).Representation);
96+
var exception = Record.Exception(() => new EnumRepresentationConvention(representation));
97+
98+
var argumentException = exception.Should().BeOfType<ArgumentException>().Subject;
99+
argumentException.ParamName.Should().Be("representation");
68100
}
69101

70-
[Fact]
71-
public void TestConventionConstruction()
102+
// private methods
103+
private BsonMemberMap CreateMemberMap<TMember>(Expression<Func<C, TMember>> member)
72104
{
73-
foreach (BsonType val in Enum.GetValues(typeof(BsonType)))
74-
{
75-
if ((val == 0) ||
76-
(val == BsonType.String) ||
77-
(val == BsonType.Int32) ||
78-
(val == BsonType.Int64))
79-
{
80-
new EnumRepresentationConvention(val);
81-
}
82-
else
83-
{
84-
Assert.Throws<ArgumentException>(() => { new EnumRepresentationConvention(val); });
85-
}
86-
}
105+
var classMap = new BsonClassMap<C>();
106+
return classMap.MapMember(member);
87107
}
88108
}
89109
}

0 commit comments

Comments
 (0)