2016-09-20 35 views
1

特性用C#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只在默認情況下使用getter和setter序列化。我知道您可以手動更改類映射並將序列化程序告訴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; 
    } 
} 

這裏是公約的代碼:

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

/// <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; 
    } 
} 

你可以使用我註冊