From c5b68d616ec30d90d1cb44e6aee71e638f9dccfe Mon Sep 17 00:00:00 2001 From: Daniel Hegener Date: Wed, 21 Jun 2017 13:19:46 +0200 Subject: [PATCH 1/4] Refactored DeserializeClass method into strategy pattern to reduce branching and shift towards an OO/composition based code style --- .../Serializers/BsonClassMapSerializer.cs | 571 +++++++++--------- 1 file changed, 301 insertions(+), 270 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs index 7c1bf94852b..e805644bdf9 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs @@ -18,7 +18,6 @@ #if !NETSTANDARD1_5 using System.ComponentModel; #endif -using System.Linq; using System.Reflection; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization.Serializers; @@ -31,32 +30,8 @@ namespace MongoDB.Bson.Serialization /// The type of the class. public class BsonClassMapSerializer : SerializerBase, IBsonIdProvider, IBsonDocumentSerializer, IBsonPolymorphicSerializer { -#if NETSTANDARD1_5 - #region static - private static void CheckForISupportInitializeInterface(out MethodInfo beginInitMethodInfo, out MethodInfo endInitMethodInfo) - { - var classTypeInfo = typeof(TClass).GetTypeInfo(); - var iSupportInitializeType = classTypeInfo.GetInterface("ISupportInitialize"); - if (iSupportInitializeType != null && iSupportInitializeType.FullName == "System.ComponentModel.ISupportInitialize") - { - var iSupportInitializeTypeInfo = iSupportInitializeType.GetTypeInfo(); - beginInitMethodInfo = iSupportInitializeTypeInfo.GetMethod("BeginInit"); - endInitMethodInfo = iSupportInitializeTypeInfo.GetMethod("EndInit"); - return; - } - - beginInitMethodInfo = null; - endInitMethodInfo = null; - } - #endregion -#endif - // private fields private BsonClassMap _classMap; -#if NETSTANDARD1_5 - private readonly MethodInfo _beginInitMethodInfo; - private readonly MethodInfo _endInitMethodInfo; -#endif // constructors /// @@ -80,9 +55,6 @@ public BsonClassMapSerializer(BsonClassMap classMap) } _classMap = classMap; -#if NETSTANDARD1_5 - CheckForISupportInitializeInterface(out _beginInitMethodInfo, out _endInitMethodInfo); -#endif } // public properties @@ -155,36 +127,19 @@ public TClass DeserializeClass(BsonDeserializationContext context) throw new FormatException(message); } - Dictionary values = null; - var document = default(TClass); -#if !NETSTANDARD1_5 - ISupportInitialize supportsInitialization = null; -#endif if (_classMap.HasCreatorMaps) { - // for creator-based deserialization we first gather the values in a dictionary and then call a matching creator - values = new Dictionary(); + return DeserializeClass(context, bsonReader, new DictionaryDeserializationStrategy(_classMap)); } else { - // for mutable classes we deserialize the values directly into the result object - document = (TClass)_classMap.CreateInstance(); -#if !NETSTANDARD1_5 - supportsInitialization = document as ISupportInitialize; - if (supportsInitialization != null) - { - supportsInitialization.BeginInit(); - } -#endif -#if NETSTANDARD1_5 - if (_beginInitMethodInfo != null) - { - _beginInitMethodInfo.Invoke(document, new object[0]); - } -#endif + return DeserializeClass(context, bsonReader, new ClassDeserializationStrategy(_classMap)); } + } + private TClass DeserializeClass(BsonDeserializationContext context, IBsonReader bsonReader, IClassDeserializationStrategy deserializationStrategy) + { var discriminatorConvention = _classMap.GetDiscriminatorConvention(); var allMemberMaps = _classMap.AllMemberMaps; var extraElementsMemberMapIndex = _classMap.ExtraElementsMemberMapIndex; @@ -203,34 +158,11 @@ public TClass DeserializeClass(BsonDeserializationContext context) var memberMap = allMemberMaps[memberMapIndex]; if (memberMapIndex != extraElementsMemberMapIndex) { - if (document != null) - { - if (memberMap.IsReadOnly) - { - bsonReader.SkipValue(); - } - else - { - var value = DeserializeMemberValue(context, memberMap); - memberMap.Setter(document, value); - } - } - else - { - var value = DeserializeMemberValue(context, memberMap); - values[elementName] = value; - } + deserializationStrategy.SetValue(context, bsonReader, elementName, memberMap); } else { - if (document != null) - { - DeserializeExtraElementMember(context, document, elementName, memberMap); - } - else - { - DeserializeExtraElementValue(context, values, elementName, memberMap); - } + deserializationStrategy.DeserializeExtraElement(context, elementName, memberMap); } memberMapBitArray[memberMapIndex >> 5] |= 1U << (memberMapIndex & 31); } @@ -245,14 +177,7 @@ public TClass DeserializeClass(BsonDeserializationContext context) if (extraElementsMemberMapIndex >= 0) { var extraElementsMemberMap = _classMap.ExtraElementsMemberMap; - if (document != null) - { - DeserializeExtraElementMember(context, document, elementName, extraElementsMemberMap); - } - else - { - DeserializeExtraElementValue(context, values, elementName, extraElementsMemberMap); - } + deserializationStrategy.DeserializeExtraElement(context, elementName, extraElementsMemberMap); memberMapBitArray[extraElementsMemberMapIndex >> 5] |= 1U << (extraElementsMemberMapIndex & 31); } else if (_classMap.IgnoreExtraElements) @@ -297,14 +222,7 @@ public TClass DeserializeClass(BsonDeserializationContext context) throw new FormatException(message); } - if (document != null) - { - memberMap.ApplyDefaultValue(document); - } - else if (memberMap.IsDefaultValueSpecified && !memberMap.IsReadOnly) - { - values[memberMap.ElementName] = memberMap.DefaultValue; - } + deserializationStrategy.ApplyDefaults(memberMap); } if (memberMapBlock == 0) @@ -319,26 +237,7 @@ public TClass DeserializeClass(BsonDeserializationContext context) } } - if (document != null) - { -#if !NETSTANDARD1_5 - if (supportsInitialization != null) - { - supportsInitialization.EndInit(); - } -#endif -#if NETSTANDARD1_5 - if (_endInitMethodInfo != null) - { - _endInitMethodInfo.Invoke(document, new object[0]); - } -#endif - return document; - } - else - { - return CreateInstanceUsingCreator(values); - } + return deserializationStrategy.GetDeserializationResult(); } /// @@ -453,165 +352,6 @@ public void SetDocumentId(object document, object id) } } - // private methods - private BsonCreatorMap ChooseBestCreator(Dictionary values) - { - // there's only one selector for now, but there might be more in the future (possibly even user provided) - var selector = new MostArgumentsCreatorSelector(); - var creatorMap = selector.SelectCreator(_classMap, values); - - if (creatorMap == null) - { - throw new BsonSerializationException("No matching creator found."); - } - - return creatorMap; - } - - private TClass CreateInstanceUsingCreator(Dictionary values) - { - var creatorMap = ChooseBestCreator(values); - var document = creatorMap.CreateInstance(values); // removes values consumed - -#if !NETSTANDARD1_5 - var supportsInitialization = document as ISupportInitialize; - if (supportsInitialization != null) - { - supportsInitialization.BeginInit(); - } -#endif - // process any left over values that weren't passed to the creator - foreach (var keyValuePair in values) - { - var elementName = keyValuePair.Key; - var value = keyValuePair.Value; - - var memberMap = _classMap.GetMemberMapForElement(elementName); - if (!memberMap.IsReadOnly) - { - memberMap.Setter.Invoke(document, value); - } - } - -#if !NETSTANDARD1_5 - if (supportsInitialization != null) - { - supportsInitialization.EndInit(); - } -#endif - - return (TClass)document; - } - - private void DeserializeExtraElementMember( - BsonDeserializationContext context, - object obj, - string elementName, - BsonMemberMap extraElementsMemberMap) - { - var bsonReader = context.Reader; - - if (extraElementsMemberMap.MemberType == typeof(BsonDocument)) - { - var extraElements = (BsonDocument)extraElementsMemberMap.Getter(obj); - if (extraElements == null) - { - extraElements = new BsonDocument(); - extraElementsMemberMap.Setter(obj, extraElements); - } - - var bsonValue = BsonValueSerializer.Instance.Deserialize(context); - extraElements[elementName] = bsonValue; - } - else - { - var extraElements = (IDictionary)extraElementsMemberMap.Getter(obj); - if (extraElements == null) - { - if (extraElementsMemberMap.MemberType == typeof(IDictionary)) - { - extraElements = new Dictionary(); - } - else - { - extraElements = (IDictionary)Activator.CreateInstance(extraElementsMemberMap.MemberType); - } - extraElementsMemberMap.Setter(obj, extraElements); - } - - var bsonValue = BsonValueSerializer.Instance.Deserialize(context); - extraElements[elementName] = BsonTypeMapper.MapToDotNetValue(bsonValue); - } - } - - private void DeserializeExtraElementValue( - BsonDeserializationContext context, - Dictionary values, - string elementName, - BsonMemberMap extraElementsMemberMap) - { - var bsonReader = context.Reader; - - if (extraElementsMemberMap.MemberType == typeof(BsonDocument)) - { - BsonDocument extraElements; - object obj; - if (values.TryGetValue(extraElementsMemberMap.ElementName, out obj)) - { - extraElements = (BsonDocument)obj; - } - else - { - extraElements = new BsonDocument(); - values.Add(extraElementsMemberMap.ElementName, extraElements); - } - - var bsonValue = BsonValueSerializer.Instance.Deserialize(context); - extraElements[elementName] = bsonValue; - } - else - { - IDictionary extraElements; - object obj; - if (values.TryGetValue(extraElementsMemberMap.ElementName, out obj)) - { - extraElements = (IDictionary)obj; - } - else - { - if (extraElementsMemberMap.MemberType == typeof(IDictionary)) - { - extraElements = new Dictionary(); - } - else - { - extraElements = (IDictionary)Activator.CreateInstance(extraElementsMemberMap.MemberType); - } - values.Add(extraElementsMemberMap.ElementName, extraElements); - } - - var bsonValue = BsonValueSerializer.Instance.Deserialize(context); - extraElements[elementName] = BsonTypeMapper.MapToDotNetValue(bsonValue); - } - } - - private object DeserializeMemberValue(BsonDeserializationContext context, BsonMemberMap memberMap) - { - var bsonReader = context.Reader; - - try - { - return memberMap.GetSerializer().Deserialize(context); - } - catch (Exception ex) - { - var message = string.Format( - "An error occurred while deserializing the {0} {1} of class {2}: {3}", // terminating period provided by nested message - memberMap.MemberName, (memberMap.MemberInfo is FieldInfo) ? "field" : "property", memberMap.ClassMap.ClassType.FullName, ex.Message); - throw new FormatException(message, ex); - } - } - private void SerializeClass(BsonSerializationContext context, BsonSerializationArgs args, TClass document) { var bsonWriter = context.Writer; @@ -762,5 +502,296 @@ public static int GetLeastSignificantBit(uint bitBlock) return leastSignificantBit - (int)(bitBlock & 1); } } + + /// + /// This type encapsulates all logic that is specific to the deserialization of TClass instances as opposed to Dictionary<string, object> instances. + /// + private class ClassDeserializationStrategy : DeserializationStrategyBase, IClassDeserializationStrategy + { +#if NETSTANDARD1_5 + #region static + private static void CheckForISupportInitializeInterface(out MethodInfo beginInitMethodInfo, out MethodInfo endInitMethodInfo) + { + var classTypeInfo = typeof(TClass).GetTypeInfo(); + var iSupportInitializeType = classTypeInfo.GetInterface("ISupportInitialize"); + if (iSupportInitializeType != null && iSupportInitializeType.FullName == "System.ComponentModel.ISupportInitialize") + { + var iSupportInitializeTypeInfo = iSupportInitializeType.GetTypeInfo(); + beginInitMethodInfo = iSupportInitializeTypeInfo.GetMethod("BeginInit"); + endInitMethodInfo = iSupportInitializeTypeInfo.GetMethod("EndInit"); + return; + } + + beginInitMethodInfo = null; + endInitMethodInfo = null; + } + #endregion +#endif + + #region private fields + private readonly TClass _document; +#if !NETSTANDARD1_5 + private readonly ISupportInitialize _supportsInitialization; +#endif +#if NETSTANDARD1_5 + private readonly MethodInfo _beginInitMethodInfo; + private readonly MethodInfo _endInitMethodInfo; +#endif + #endregion + + public ClassDeserializationStrategy(BsonClassMap classMap) : base(classMap) + { + // for mutable classes we deserialize the values directly into the result object + _document = (TClass)ClassMap.CreateInstance(); + +#if !NETSTANDARD1_5 + _supportsInitialization = _document as ISupportInitialize; + if (_supportsInitialization != null) + { + _supportsInitialization.BeginInit(); + } +#endif +#if NETSTANDARD1_5 + CheckForISupportInitializeInterface(out _beginInitMethodInfo, out _endInitMethodInfo); + if (_beginInitMethodInfo != null) + { + _beginInitMethodInfo.Invoke(_document, new object[0]); + } +#endif + } + + public TClass GetDeserializationResult() + { +#if !NETSTANDARD1_5 + if (_supportsInitialization != null) + { + _supportsInitialization.EndInit(); + } +#endif +#if NETSTANDARD1_5 + if (_endInitMethodInfo != null) + { + _endInitMethodInfo.Invoke(_document, new object[0]); + } +#endif + return _document; + } + + public void SetValue(BsonDeserializationContext context, IBsonReader bsonReader, string elementName, BsonMemberMap memberMap) + { + if (memberMap.IsReadOnly) + { + bsonReader.SkipValue(); + } + else + { + var value = DeserializeMemberValue(context, memberMap); + memberMap.Setter(_document, value); + } + } + + public void ApplyDefaults(BsonMemberMap memberMap) + { + memberMap.ApplyDefaultValue(_document); + } + + public void DeserializeExtraElement(BsonDeserializationContext context, string elementName, BsonMemberMap extraElementsMemberMap) + { + if (extraElementsMemberMap.MemberType == typeof(BsonDocument)) + { + var extraElements = (BsonDocument)extraElementsMemberMap.Getter(_document); + if (extraElements == null) + { + extraElements = new BsonDocument(); + extraElementsMemberMap.Setter(_document, extraElements); + } + + var bsonValue = BsonValueSerializer.Instance.Deserialize(context); + extraElements[elementName] = bsonValue; + } + else + { + var extraElements = (IDictionary)extraElementsMemberMap.Getter(_document); + if (extraElements == null) + { + if (extraElementsMemberMap.MemberType == typeof(IDictionary)) + { + extraElements = new Dictionary(); + } + else + { + extraElements = (IDictionary)Activator.CreateInstance(extraElementsMemberMap.MemberType); + } + extraElementsMemberMap.Setter(_document, extraElements); + } + + var bsonValue = BsonValueSerializer.Instance.Deserialize(context); + extraElements[elementName] = BsonTypeMapper.MapToDotNetValue(bsonValue); + } + } + } + + /// + /// This type encapsulates all logic that is specific to the deserialization of Dictionary<string, object> instances as opposed to TClass instances. + /// + private class DictionaryDeserializationStrategy : DeserializationStrategyBase, IClassDeserializationStrategy + { + private readonly Dictionary _values; + + public DictionaryDeserializationStrategy(BsonClassMap classMap) : base(classMap) + { + // for creator-based deserialization we first gather the values in a dictionary and then call a matching creator + _values = new Dictionary(); + } + + public TClass GetDeserializationResult() + { + return CreateInstanceUsingCreator(_values); + } + + public void SetValue(BsonDeserializationContext context, IBsonReader bsonReader, string elementName, BsonMemberMap memberMap) + { + var value = DeserializeMemberValue(context, memberMap); + _values[elementName] = value; + } + + public void ApplyDefaults(BsonMemberMap memberMap) + { + if (memberMap.IsDefaultValueSpecified && !memberMap.IsReadOnly) + { + _values[memberMap.ElementName] = memberMap.DefaultValue; + } + } + + public void DeserializeExtraElement(BsonDeserializationContext context, string elementName, BsonMemberMap extraElementsMemberMap) + { + if (extraElementsMemberMap.MemberType == typeof(BsonDocument)) + { + BsonDocument extraElements; + object obj; + if (_values.TryGetValue(extraElementsMemberMap.ElementName, out obj)) + { + extraElements = (BsonDocument)obj; + } + else + { + extraElements = new BsonDocument(); + _values.Add(extraElementsMemberMap.ElementName, extraElements); + } + + var bsonValue = BsonValueSerializer.Instance.Deserialize(context); + extraElements[elementName] = bsonValue; + } + else + { + IDictionary extraElements; + object obj; + if (_values.TryGetValue(extraElementsMemberMap.ElementName, out obj)) + { + extraElements = (IDictionary)obj; + } + else + { + if (extraElementsMemberMap.MemberType == typeof(IDictionary)) + { + extraElements = new Dictionary(); + } + else + { + extraElements = (IDictionary)Activator.CreateInstance(extraElementsMemberMap.MemberType); + } + _values.Add(extraElementsMemberMap.ElementName, extraElements); + } + + var bsonValue = BsonValueSerializer.Instance.Deserialize(context); + extraElements[elementName] = BsonTypeMapper.MapToDotNetValue(bsonValue); + } + } + + private BsonCreatorMap ChooseBestCreator(Dictionary values) + { + // there's only one selector for now, but there might be more in the future (possibly even user provided) + var selector = new MostArgumentsCreatorSelector(); + var creatorMap = selector.SelectCreator(ClassMap, values); + + if (creatorMap == null) + { + throw new BsonSerializationException("No matching creator found."); + } + + return creatorMap; + } + + private TClass CreateInstanceUsingCreator(Dictionary values) + { + var creatorMap = ChooseBestCreator(values); + var document = creatorMap.CreateInstance(values); // removes values consumed + +#if !NETSTANDARD1_5 + var supportsInitialization = document as ISupportInitialize; + if (supportsInitialization != null) + { + supportsInitialization.BeginInit(); + } +#endif + // process any left over values that weren't passed to the creator + foreach (var keyValuePair in values) + { + var elementName = keyValuePair.Key; + var value = keyValuePair.Value; + + var memberMap = ClassMap.GetMemberMapForElement(elementName); + if (!memberMap.IsReadOnly) + { + memberMap.Setter.Invoke(document, value); + } + } + +#if !NETSTANDARD1_5 + if (supportsInitialization != null) + { + supportsInitialization.EndInit(); + } +#endif + + return (TClass)document; + } + } + + private interface IClassDeserializationStrategy + { + void SetValue(BsonDeserializationContext context, IBsonReader bsonReader, string elementName, BsonMemberMap memberMap); + void DeserializeExtraElement(BsonDeserializationContext context, string elementName, BsonMemberMap memberMap); + void ApplyDefaults(BsonMemberMap memberMap); + TClass GetDeserializationResult(); + } + + /// + /// This type contains code that is shared between both the TClass based deserialization logic as well as the Dictionary<string, object> based one. + /// + private abstract class DeserializationStrategyBase + { + protected BsonClassMap ClassMap { get; } + + protected DeserializationStrategyBase(BsonClassMap classMap) + { + ClassMap = classMap; + } + + protected object DeserializeMemberValue(BsonDeserializationContext context, BsonMemberMap memberMap) + { + try + { + return memberMap.GetSerializer().Deserialize(context); + } + catch (Exception ex) + { + var message = string.Format( + "An error occurred while deserializing the {0} {1} of class {2}: {3}", // terminating period provided by nested message + memberMap.MemberName, (memberMap.MemberInfo is FieldInfo) ? "field" : "property", memberMap.ClassMap.ClassType.FullName, ex.Message); + throw new FormatException(message, ex); + } + } + } } } From 3a0936bf39233e5671e7721c85ed86e5200fa648 Mon Sep 17 00:00:00 2001 From: Daniel Hegener Date: Wed, 9 Dec 2020 06:03:06 +0100 Subject: [PATCH 2/4] ReSharper code clean-up only for conciseness --- .../Serializers/BsonClassMapSerializer.cs | 72 ++++++------------- 1 file changed, 22 insertions(+), 50 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs index e805644bdf9..8adf1bf9b08 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs @@ -86,27 +86,22 @@ public override TClass Deserialize(BsonDeserializationContext context, BsonDeser throw new BsonSerializationException(message); } - if (bsonReader.GetCurrentBsonType() == Bson.BsonType.Null) + if (bsonReader.GetCurrentBsonType() == BsonType.Null) { bsonReader.ReadNull(); - return default(TClass); + return default; } - else - { - var discriminatorConvention = _classMap.GetDiscriminatorConvention(); - var actualType = discriminatorConvention.GetActualType(bsonReader, args.NominalType); - if (actualType == typeof(TClass)) - { - return DeserializeClass(context); - } - else - { - var serializer = BsonSerializer.LookupSerializer(actualType); - return (TClass)serializer.Deserialize(context); - } + var discriminatorConvention = _classMap.GetDiscriminatorConvention(); + var actualType = discriminatorConvention.GetActualType(bsonReader, args.NominalType); + if (actualType == typeof(TClass)) + { + return DeserializeClass(context); } + + var serializer = BsonSerializer.LookupSerializer(actualType); + return (TClass)serializer.Deserialize(context); } /// @@ -131,11 +126,8 @@ public TClass DeserializeClass(BsonDeserializationContext context) { return DeserializeClass(context, bsonReader, new DictionaryDeserializationStrategy(_classMap)); } - else - { - return DeserializeClass(context, bsonReader, new ClassDeserializationStrategy(_classMap)); - } + return DeserializeClass(context, bsonReader, new ClassDeserializationStrategy(_classMap)); } private TClass DeserializeClass(BsonDeserializationContext context, IBsonReader bsonReader, IClassDeserializationStrategy deserializationStrategy) @@ -262,13 +254,11 @@ public bool GetDocumentId( idGenerator = idMemberMap.IdGenerator; return true; } - else - { - id = null; - idNominalType = null; - idGenerator = null; - return false; - } + + id = null; + idNominalType = null; + idGenerator = null; + return false; } /// @@ -546,33 +536,21 @@ public ClassDeserializationStrategy(BsonClassMap classMap) : base(classMap) #if !NETSTANDARD1_5 _supportsInitialization = _document as ISupportInitialize; - if (_supportsInitialization != null) - { - _supportsInitialization.BeginInit(); - } + _supportsInitialization?.BeginInit(); #endif #if NETSTANDARD1_5 CheckForISupportInitializeInterface(out _beginInitMethodInfo, out _endInitMethodInfo); - if (_beginInitMethodInfo != null) - { - _beginInitMethodInfo.Invoke(_document, new object[0]); - } + _beginInitMethodInfo?.Invoke(_document, new object[0]); #endif } public TClass GetDeserializationResult() { #if !NETSTANDARD1_5 - if (_supportsInitialization != null) - { - _supportsInitialization.EndInit(); - } + _supportsInitialization?.EndInit(); #endif #if NETSTANDARD1_5 - if (_endInitMethodInfo != null) - { - _endInitMethodInfo.Invoke(_document, new object[0]); - } + _endInitMethodInfo?.Invoke(_document, new object[0]); #endif return _document; } @@ -729,10 +707,7 @@ private TClass CreateInstanceUsingCreator(Dictionary values) #if !NETSTANDARD1_5 var supportsInitialization = document as ISupportInitialize; - if (supportsInitialization != null) - { - supportsInitialization.BeginInit(); - } + supportsInitialization?.BeginInit(); #endif // process any left over values that weren't passed to the creator foreach (var keyValuePair in values) @@ -748,10 +723,7 @@ private TClass CreateInstanceUsingCreator(Dictionary values) } #if !NETSTANDARD1_5 - if (supportsInitialization != null) - { - supportsInitialization.EndInit(); - } + supportsInitialization?.EndInit(); #endif return (TClass)document; From 480aa118246fc28a21052771d174bbc1636fd109 Mon Sep 17 00:00:00 2001 From: Daniel Hegener Date: Wed, 9 Dec 2020 06:21:37 +0100 Subject: [PATCH 3/4] Performance: Reduced the amount of repeated reflection calls by leveraging static variables inside the generic BsonClassMapSerializer.ClassDeserializationStrategy type --- .../Serializers/BsonClassMapSerializer.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs index 8adf1bf9b08..f80efd933d3 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs @@ -516,16 +516,23 @@ private static void CheckForISupportInitializeInterface(out MethodInfo beginInit endInitMethodInfo = null; } #endregion + + #region private static fields + private static readonly MethodInfo _beginInitMethodInfo; + private static readonly MethodInfo _endInitMethodInfo; + #endregion + + // Note: This is inside a generic type so the static constructor will be called on a per type parameter (TClass) basis. + static ClassDeserializationStrategy() + { + CheckForISupportInitializeInterface(out _beginInitMethodInfo, out _endInitMethodInfo); + } #endif #region private fields private readonly TClass _document; #if !NETSTANDARD1_5 private readonly ISupportInitialize _supportsInitialization; -#endif -#if NETSTANDARD1_5 - private readonly MethodInfo _beginInitMethodInfo; - private readonly MethodInfo _endInitMethodInfo; #endif #endregion @@ -539,7 +546,6 @@ public ClassDeserializationStrategy(BsonClassMap classMap) : base(classMap) _supportsInitialization?.BeginInit(); #endif #if NETSTANDARD1_5 - CheckForISupportInitializeInterface(out _beginInitMethodInfo, out _endInitMethodInfo); _beginInitMethodInfo?.Invoke(_document, new object[0]); #endif } From 2cea6498ccbf21cabd632c34dfcfd1e9e726a248 Mon Sep 17 00:00:00 2001 From: Daniel Hegener Date: Wed, 9 Dec 2020 06:22:01 +0100 Subject: [PATCH 4/4] Made the _classMap field readonly --- .../Serialization/Serializers/BsonClassMapSerializer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs index f80efd933d3..d80b572ee36 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/BsonClassMapSerializer.cs @@ -31,7 +31,7 @@ namespace MongoDB.Bson.Serialization public class BsonClassMapSerializer : SerializerBase, IBsonIdProvider, IBsonDocumentSerializer, IBsonPolymorphicSerializer { // private fields - private BsonClassMap _classMap; + private readonly BsonClassMap _classMap; // constructors ///