2016-09-20 12 views
1

С # 6 Я могу написать:сериализации получить свойства только на MongoDb

public class Person 
{ 
    public Guid Id { get; } 
    public string Name { get; } 
    public Person(Guid id, string name) 
    { 
     Id = id; 
     Name = name; 
    } 
} 

К сожалению, класс, как это не сериализовать правильно драйвером MongoDB, свойства не сериализуются.

MongoDb только сериализует свойства по умолчанию с помощью геттера и сеттера. Я знал, что вы можете вручную изменить сопоставление классов и рассказать serializer к include get-only properties, но я искал общий способ избежать настройки каждого сопоставления.

Я думал создать обычную конвенцию, похожую на ReadWriteMemberFinderConvention, но без проверки CanWrite.

Есть и другие решения? Конструктор будет вызван автоматически или мне нужна другая настройка?

ответ

3

Я попытался решить эту проблему, создав соглашение, которое отображает все свойства только для чтения, соответствующие конструктору, а также сопоставленный конструктор.

Предположим, что у вас есть неизменный класс, как:

public class Person 
{ 
    public string FirstName { get; } 
    public string LastName { get; } 
    public string FullName => FirstName + LastName; 
    public ImmutablePocoSample(string lastName) 
    { 
     LastName = lastName; 
    } 

    public ImmutablePocoSample(string firstName, string lastName) 
    { 
     FirstName = firstName; 
     LastName = lastName; 
    } 
} 

Вот код конвенции:

/// <summary> 
/// A convention that map all read only properties for which a matching constructor is found. 
/// Also matching constructors are mapped. 
/// </summary> 
public class ImmutablePocoConvention : ConventionBase, IClassMapConvention 
{ 
    private readonly BindingFlags _bindingFlags; 

    public ImmutablePocoConvention() 
      : this(BindingFlags.Instance | BindingFlags.Public) 
    { } 

    public ImmutablePocoConvention(BindingFlags bindingFlags) 
    { 
     _bindingFlags = bindingFlags | BindingFlags.DeclaredOnly; 
    } 

    public void Apply(BsonClassMap classMap) 
    { 
     var readOnlyProperties = classMap.ClassType.GetTypeInfo() 
      .GetProperties(_bindingFlags) 
      .Where(p => IsReadOnlyProperty(classMap, p)) 
      .ToList(); 

     foreach (var constructor in classMap.ClassType.GetConstructors()) 
     { 
      // If we found a matching constructor then we map it and all the readonly properties 
      var matchProperties = GetMatchingProperties(constructor, readOnlyProperties); 
      if (matchProperties.Any()) 
      { 
       // Map constructor 
       classMap.MapConstructor(constructor); 

       // Map properties 
       foreach (var p in matchProperties) 
        classMap.MapMember(p); 
      } 
     } 
    } 

    private static List<PropertyInfo> GetMatchingProperties(ConstructorInfo constructor, List<PropertyInfo> properties) 
    { 
     var matchProperties = new List<PropertyInfo>(); 

     var ctorParameters = constructor.GetParameters(); 
     foreach (var ctorParameter in ctorParameters) 
     { 
      var matchProperty = properties.FirstOrDefault(p => ParameterMatchProperty(ctorParameter, p)); 
      if (matchProperty == null) 
       return new List<PropertyInfo>(); 

      matchProperties.Add(matchProperty); 
     } 

     return matchProperties; 
    } 


    private static bool ParameterMatchProperty(ParameterInfo parameter, PropertyInfo property) 
    { 
     return string.Equals(property.Name, parameter.Name, System.StringComparison.InvariantCultureIgnoreCase) 
       && parameter.ParameterType == property.PropertyType; 
    } 

    private static bool IsReadOnlyProperty(BsonClassMap classMap, PropertyInfo propertyInfo) 
    { 
     // we can't read 
     if (!propertyInfo.CanRead) 
      return false; 

     // we can write (already handled by the default convention...) 
     if (propertyInfo.CanWrite) 
      return false; 

     // skip indexers 
     if (propertyInfo.GetIndexParameters().Length != 0) 
      return false; 

     // skip overridden properties (they are already included by the base class) 
     var getMethodInfo = propertyInfo.GetMethod; 
     if (getMethodInfo.IsVirtual && getMethodInfo.GetBaseDefinition().DeclaringType != classMap.ClassType) 
      return false; 

     return true; 
    } 
} 

Вы можете зарегистрировать я с помощью:

ConventionRegistry.Register(
    nameof(ImmutablePocoConvention), 
    new ConventionPack { new ImmutablePocoConvention() }, 
    _ => true); 

 Смежные вопросы

  • Нет связанных вопросов^_^