2013-10-22 15 views
6

Trong ứng dụng của tôi, tôi phải sử dụng ExpandoObject để tạo/xóa các thuộc tính trong suốt thời gian chạy; Tuy nhiên, tôi phải ánh xạ ExpandoObject trả về của một hàm cho đối tượng/lớp tương ứng. Vì vậy, tôi đã đưa ra một Mapper nhỏ mà thực hiện công việc nhưng với 3 vấn đề:Ánh xạ đệ quy ExpandoObject

  1. Nó không đệ quy đệ quy các đối tượng bên trong của ExpandoObject như được cho là.
  2. Khi tôi cố gắng ánh xạ int vào một Nullable chỉ đơn giản là nó sẽ ném một loại không phù hợp vì tôi không thể tìm thấy một cách để phát hiện và đúc nó đúng cách.
  3. Các trường không thể được ánh xạ public string Property;.

Code:

I- Thực hiện:

public static class Mapper<T> where T : class 
{ 
    #region Properties 

    private static readonly Dictionary<string, PropertyInfo> PropertyMap; 

    #endregion 

    #region Ctor 

    static Mapper() { PropertyMap = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToDictionary(p => p.Name.ToLower(), p => p); } 

    #endregion 

    #region Methods 

    public static void Map(ExpandoObject source, T destination) 
    { 
     if (source == null) 
      throw new ArgumentNullException("source"); 
     if (destination == null) 
      throw new ArgumentNullException("destination"); 

     foreach (var kv in source) 
     { 
      PropertyInfo p; 
      if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p)) 
      { 
       Type propType = p.PropertyType; 
       if (kv.Value == null) 
       { 
        if (!propType.IsByRef && propType.Name != "Nullable`1") 
        { 
         throw new ArgumentException("not nullable"); 
        } 
       } 
       else if (kv.Value.GetType() != propType) 
       { 
        throw new ArgumentException("type mismatch"); 
       } 
       p.SetValue(destination, kv.Value, null); 
      } 
     } 
    } 

    #endregion 
} 

II: Cách sử dụng:

public static void Main() 
{ 
    Class c = new Class(); 
    dynamic o = new ExpandoObject(); 
    o.Name = "Carl"; 
    o.Level = 7; 
    o.Inner = new InnerClass 
       { 
         Name = "Inner Carl", 
         Level = 10 
       }; 

    Mapper<Class>.Map(o, c); 

    Console.Read(); 
} 

internal class Class 
{ 
    public string Name { get; set; } 
    public int? Level { get; set; } 
    public InnerClass Inner { get; set; } 
    public string Property; 
} 

internal class InnerClass 
{ 
    public string Name { get; set; } 
    public int? Level { get; set; } 
} 
+0

Bất kỳ câu trả lời ... –

Trả lời

4

3- Nếu tài sản được hình thành như thế này public string Property; các thuộc tính nhận được không nhận được nó.

Ồ, đó không phải là tài sản, đó là một trường. Nếu bạn muốn xem xét các lĩnh vực là tốt.

static Mapper() 
{ 
    PropertyMap = typeof(T).GetProperties(BindingFlags.Public | 
               BindingFlags.NonPublic | 
               BindingFlags.Instance) 
               .ToDictionary(p => p.Name.ToLower(), p => p); 

    FieldMap = typeof(T).GetFields(BindingFlags.Public | 
               BindingFlags.NonPublic | 
               BindingFlags.Instance) 
               .ToDictionary(f => f.Name.ToLower(), f => f); 
} 

2- Khi tôi cố gắng để lập bản đồ int để một Nullable chỉ đơn giản là nó sẽ ném một loại không phù hợp, vì tôi không thể tìm thấy một cách để phát hiện và đúc nó đúng cách.

Tại sao chọn loại Nullable, hãy phản ánh con số đó. Nếu giá trị hợp lệ, nó sẽ được gán.

public static void Map(ExpandoObject source, T destination) 
{ 
    if (source == null) 
     throw new ArgumentNullException("source"); 
    if (destination == null) 
     throw new ArgumentNullException("destination"); 

    foreach (var kv in source) 
    { 
     PropertyInfo p; 
     if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p)) 
     { 
      p.SetValue(destination, kv.Value, null); 
     } 
     else 
     { 
      FieldInfo f; 
      if (FieldMap.TryGetValue(kv.Key.ToLower(), out f)) 
      { 
       f.SetValue(destination, kv.Value); 
      } 
     } 
    } 
} 

1 - Nó không đệ quy bản đồ các đối tượng bên trong của ExpandoObject như vụ.

Dường như làm việc cho bạn InnerClass ít nhất.

Class c = new Class(); 
dynamic o = new ExpandoObject(); 
o.Name = "Carl"; 
o.Level = 7; 
o.Inner = new InnerClass 
{ 
    Name = "Inner Carl", 
    Level = 10 
}; 

o.Property = "my Property value"; // dont forget to set this 

Mapper<Class>.Map(o, c); 

EDIT: dựa trên ý kiến ​​của bạn, tôi đã tạo ra hai phương pháp quá tải MergeProperty. Bạn có thể viết các phương thức quá tải tương tự cho các trường.

public static void MergeProperty(PropertyInfo pi, ExpandoObject source, object target) 
{ 
    Type propType = pi.PropertyType; 

    // dont recurse for value type, Nullable<T> and strings 
    if (propType.IsValueType || propType == typeof(string)) 
    { 
     var sourceVal = source.First(kvp => kvp.Key == pi.Name).Value; 
     if(sourceVal != null) 
      pi.SetValue(target, sourceVal, null); 
    } 
    else // recursively map inner class properties 
    { 
     var props = propType.GetProperties(BindingFlags.Public | 
                BindingFlags.NonPublic | 
                BindingFlags.Instance); 

     foreach (var p in props) 
     { 
      var sourcePropValue = source.First(kvp => kvp.Key == pi.Name).Value; 
      var targetPropValue = pi.GetValue(target, null); 

      if (sourcePropValue != null) 
      { 
       if (targetPropValue == null) // replace 
       { 
        pi.SetValue(target, source.First(kvp => kvp.Key == pi.Name).Value, null); 
       } 
       else 
       { 
        MergeProperty(p, sourcePropValue, targetPropValue); 
       } 
      } 
     } 

    } 
} 

public static void MergeProperty(PropertyInfo pi, object source, object target) 
{ 
    Type propType = pi.PropertyType; 
    PropertyInfo sourcePi = source.GetType().GetProperty(pi.Name); 

    // dont recurse for value type, Nullable<T> and strings 
    if (propType.IsValueType || propType == typeof(string)) 
    { 
     var sourceVal = sourcePi.GetValue(source, null); 
     if(sourceVal != null) 
      pi.SetValue(target, sourceVal, null); 
    } 
    else // recursively map inner class properties 
    { 
     var props = propType.GetProperties(BindingFlags.Public | 
                BindingFlags.NonPublic | 
                BindingFlags.Instance); 

     foreach (var p in props) 
     { 
      var sourcePropValue = sourcePi.GetValue(source, null); 
      var targetPropValue = pi.GetValue(target, null); 

      if (sourcePropValue != null) 
      { 
       if (targetPropValue == null) // replace 
       { 
        pi.SetValue(target, sourcePi.GetValue(source, null), null); 
       } 
       else 
       { 
        MergeProperty(p, sourcePropValue, targetPropValue); 
       } 
      } 
     } 

    } 
} 

Bạn có thể sử dụng các phương pháp như sau:?

public static void Map(ExpandoObject source, T destination) 
{ 
    if (source == null) 
     throw new ArgumentNullException("source"); 
    if (destination == null) 
     throw new ArgumentNullException("destination"); 

    foreach (var kv in source) 
    { 
     PropertyInfo p; 
     if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p)) 
     { 
      MergeProperty(p, source, destination); 
     } 
     else 
     { 
      // do similar merge for fields 
     } 
    } 
} 
+0

Vâng đẹp câu trả lời chi tiết, Tuy nhiên, nó sẽ làm việc với InnerClass nhưng nó sẽ thay thế nó không Map nó. .. như tôi muốn cập nhật (map) các giá trị như nó đã làm với phần còn lại của các thuộc tính không bên trong [nói cách khác (bỏ qua các giá trị null từ nguồn và không thay thế các giá trị hiện có bằng null)].Ngoài ra, hãy xem xét sắp xếp câu trả lời 1,2,3 thay vì 3,2,1: D –

+0

Bạn có thể xây dựng câu trả lời 'nó sẽ thay thế nó không Map nó'? Bạn có muốn sao chép không? Làm thế nào 'các giá trị bên trong không được ánh xạ và không được thay thế? Tôi không hiểu. – YK1

+0

mmm, những gì tôi muốn nói là. Công việc của người lập bản đồ đó là cập nhật các trường/thuộc tính đích, và ý nghĩa của tôi với bản cập nhật là nếu người lập bản đồ tìm thấy trường/thuộc tính rỗng trong nguồn, nó sẽ không thay thế trường/thuộc tính đích tương ứng bằng null mà chỉ giữ nó, nếu không nó sẽ cập nhật nó. Thứ hai những gì tôi thiếu ở đây là tôi muốn người lập bản đồ nhìn xem trường/tài sản có chứa các thuộc tính bên trong hơn không, nó cũng làm tương tự với nó (đây là ý nghĩa của tôi với ánh xạ đệ quy) –