Наконец-то я нашел решение, оно находится между исходным решением 2 и 3.
Основная идея состоит в том, чтобы найти всю структуру в структуре данных «клиента» и зарегистрировать для нее специальный сериализатор структуры.,Задачи заключаются в следующем:
Найти все типы структуры в структуре данных "клиента"
Необходимо найти рекурсивно, даже если структура является частью коллекции, которая являетсяспрятан в классе, который покрыт коллекциями и т. д. Так что нам нужно было найти его во всех случаях.К счастью, MongoDB помогает найти все экземпляры, потому что во время сериализации MongoDB делает рекурсивное прохождение по каждому типу.Поэтому мы регистрируем провайдера сериализации, который «обнаруживает» всю структуру и предоставляет для него специальный сериализатор.
Обнаружить, что данный тип является структурой или нет
Для этогоработа, было много ответов на StackOverflow, из них идеально было .Возможно, мое решение тоже не идеальное, но мы объединили все идеи.Таким образом, мы проверяем, что тип не является примитивным, это не enum, но это тип значения, а не некоторая структура по умолчанию, которая уже имеет сериализатор в MongoDB.
Коды следующие:
1, Зарегистрируйте поставщика сериализатора для MongoDB:
BsonSerializer.RegisterSerializationProvider( new MongoDB_SerializationProvider() );
2, Реализуйте сериализатор:
class MongoDB_SerializationProvider : BsonSerializationProviderBase
{
private static readonly object locker = new object();
private static Dictionary<Type, MongoDB_StructSerializer> _StructSerializers;
private static MongoDB_DecimalSerializer _DecimalSerializer;
static MongoDB_SerializationProvider()
{
_StructSerializers = new Dictionary<Type, MongoDB_StructSerializer>();
_DecimalSerializer = new MongoDB_DecimalSerializer();
}
public override IBsonSerializer GetSerializer( Type type, IBsonSerializerRegistry serializerRegistry )
{
if ( type == typeof( decimal ) )
{
return _DecimalSerializer;
}
else if ( Reflection.Info.IsStruct( type ) && type != typeof( ObjectId ) )
{
MongoDB_StructSerializer structSerializer = null;
lock ( locker )
{
if ( _StructSerializers.TryGetValue( type, out structSerializer ) == false )
{
structSerializer = new MongoDB_StructSerializer( type );
_StructSerializers.Add( type, structSerializer );
}
}
return structSerializer;
}
else
{
return null;
}
}
}
Десятичная часть - еще одна интересная тема, но она не является частью текущего вопроса.Мы должны быть осторожны с одной вещью: ObjectId MongoDB также является структурой, и мы, конечно, не хотим регистрировать сериализатор для ObjectId.В коде есть функция, которая делает немного магии: Reflection.Info.IsStruct( type )
Вот ее код:
public static bool IsStruct( Type type )
{
if ( IsPrimitiveType( type ) == true )
return false;
if ( type.IsValueType == false )
return false;
return true;
}
static public bool IsPrimitiveType( Type type )
{
if ( type.GetTypeInfo().IsPrimitive == true )
return true;
if ( type.GetTypeInfo().IsEnum == true )
return true;
if ( type == typeof( decimal ) )
return true;
if ( type == typeof( string ) )
return true;
if ( type == typeof( DateTime ) )
return true;
if ( type == typeof( DateTimeOffset ) )
return true;
if ( type == typeof( TimeSpan ) )
return true;
if ( type == typeof( Guid ) )
return true;
return false;
}
3, Реализация Сериализатора
Этонемного длиннее кода, но я надеюсь, что он все еще понятен:
public class MongoDB_StructSerializer : IBsonSerializer
{
public Type ValueType { get; }
public MongoDB_StructSerializer( Type valueType )
{
ValueType = valueType;
}
public void Serialize( BsonSerializationContext context, BsonSerializationArgs args, object value )
{
if ( value == null )
{
context.Writer.WriteNull();
}
else
{
List<MemberInfo> members = Reflection.Serialize.GetAllSerializableMembers( ValueType );
context.Writer.WriteStartDocument();
foreach( MemberInfo member in members )
{
context.Writer.WriteName( member.Name );
BsonSerializer.Serialize( context.Writer, Reflection.Info.GetMemberType( member ), Reflection.Info.GetMemberValue( member, value ), null, args );
}
context.Writer.WriteEndDocument();
}
}
public object Deserialize( BsonDeserializationContext context, BsonDeserializationArgs args )
{
BsonType bsonType = context.Reader.GetCurrentBsonType();
if ( bsonType == BsonType.Null )
{
context.Reader.ReadNull();
return null;
}
else
{
object obj = Activator.CreateInstance( ValueType );
context.Reader.ReadStartDocument();
while ( context.Reader.ReadBsonType() != BsonType.EndOfDocument )
{
string name = context.Reader.ReadName();
FieldInfo field = ValueType.GetField( name );
if ( field != null )
{
object value = BsonSerializer.Deserialize( context.Reader, field.FieldType );
field.SetValue( obj, value );
}
PropertyInfo prop = ValueType.GetProperty( name );
if ( prop != null )
{
object value = BsonSerializer.Deserialize( context.Reader, prop.PropertyType );
prop.SetValue( obj, value, null );
}
}
context.Reader.ReadEndDocument();
return obj;
}
}
}
Волшебная функция: Reflection.Serialize.GetAllSerializableMembers
содержит некоторые действительно интересные вещи, что такое сериализуемый член, а что нет.
public static List<MemberInfo> GetSerializableMembers( Type type, BindingFlags bindingFlags )
{
List<MemberInfo> list = new List<MemberInfo>();
FieldInfo[] fields = type.GetFields( bindingFlags );
foreach ( FieldInfo field in fields )
{
if ( IsFieldSerializable( type, field ) == false )
continue;
list.Add( field );
}
PropertyInfo[] properties = type.GetProperties( bindingFlags );
foreach ( PropertyInfo property in properties )
{
if ( IsPropertySerializable( type, property ) == false )
continue;
list.Add( property );
}
return list;
}
public static bool IsFieldSerializable( Type type, FieldInfo field )
{
if ( field.IsInitOnly == true )
return false;
if ( field.IsLiteral == true )
return false;
if ( field.IsDefined( typeof( CompilerGeneratedAttribute ), false ) == true )
return false;
if ( field.IsDefined( typeof( IgnoreAttribute ), false ) == true )
return false;
return true;
}
public static bool IsPropertySerializable( Type type, PropertyInfo property )
{
if ( property.CanRead == false )
return false;
if ( property.CanWrite == false )
return false;
if ( property.GetIndexParameters().Length != 0 )
return false;
if ( property.GetMethod.IsVirtual && property.GetMethod.GetBaseDefinition().DeclaringType != type )
return false;
if ( property.IsDefined( typeof( IgnoreAttribute ), false ) == true )
return false;
return true;
}
Резюме
Это решение хорошо зарекомендовало себя (около 15-20 различных тестовых случаев) и хорошо работает.Я думаю, что сообщество MongoDB также может реализовать сериализацию структуры.Они печальны, что это не может быть сделано, потому что структура - это valutypes, поэтому значения копируются не по ссылке, поэтому, когда одна функция изменяет значение внутри, оригинал не изменяется.Но!Весь код сериализации внутри MongoDB использует «объект» и структуры также являются объектами.И нигде в коде драйвера нет изменений членов.Только в десериализации, которая перезаписана в нашем коде.
Так что сообщество MongoDB может сделать это, если они этого захотят!:)
PS Чем читать длинный пост, здесь есть Картошка