2014-11-03 44 views
2

我正試圖設計一個進程來提取轉換加載操作。我想在我的管道中使用ExpandoObject,以便輕鬆地將列添加到我的數據流中。基本上,我從某種數據源讀取數據,將其轉換爲動態數據並將其返回給變換管道,這些變換可以基於現有屬性或其他屬性添加屬性,然後將數據流送入數據庫。使用DynamicObject存儲類型信息

我遇到的問題是我需要添加到我的expando對象的所有屬性的類型信息,即使我添加了Nullable類型。如果Nullable類型因值的裝箱而爲空,則這會丟失。我需要這些類型信息,以便在我的管道結束時,我可以通過枚舉ExpandoObjects實現一個datareader並將數據流式傳輸到數據庫中。

我希望SetMemberBinder.ReturnType屬性可以幫助我,但它似乎返回一個對象。

這裏的一些示例代碼:

using System; 
using System.Collections.Generic; 
using System.Dynamic; 

using Xunit 

namespace Whanger 
{ 
    public class MyExpando : DynamicObject 
    { 
     Dictionary<string, object> properties = new Dictionary<string, object>(); 
     Dictionary<string, Type> propertyTypes = new Dictionary<string, Type>(); 

     public Dictionary<string, Type> Types 
     { 
      get 
      { 
       return propertyTypes; 
      } 
     } 

     public Dictionary<string, object> Values 
     { 
      get 
      { 
       return properties; 
      } 
     } 

     public override bool TryGetMember(GetMemberBinder binder, out object result) 
     { 
      if (properties.ContainsKey(binder.Name)) 
      { 
       result = properties[binder.Name]; 
       return true; 
      } 
      else 
      { 
       result = null; 
       return false; 
      } 
     } 

     public override bool TrySetMember(SetMemberBinder binder, object value) 
     { 
      properties[binder.Name] = value; 
      propertyTypes[binder.Name] = binder.ReturnType;//always object :(
      return true; 
     } 

     public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) 
     { 
      dynamic method = properties[binder.Name]; 
      result = method(args[0].ToString(), args[1].ToString()); 
      return true; 
     } 
    } 

    public class MyExpandoTests 
    { 
     [Fact] 
     public void CanAddDynamicMembers() 
     { 
      dynamic obj = new MyExpando(); 
      obj.Name = "Wibble"; 
      obj.Value = 2; 

      Assert.Equal(obj.Name, "Wibble"); 
      Assert.Equal(obj.Value, 2); 
     } 

     [Fact] 
     public void CanMaintainType() 
     { 
      dynamic obj = new MyExpando(); 
      int? nullableInt = null; 
      obj.NullInt = nullableInt; 
      obj.Name = "Wibble"; 
      Assert.Equal(obj.Name, "Wibble"); 
      Assert.Null(obj.NullInt); 
      //fails 
      Assert.Equal(typeof(int?), ((MyExpando)obj).Types["NullInt"]); 


     } 
    } 
} 

有沒有辦法找出從TrySetMember類型?我想知道是否有某種方法可以使用某種表達樹魔術?

如果任何人有任何偉大的想法,我很樂意聽到他們。除了可爲空的類型外,它們都運行良好,但它們是數據庫操作的關鍵。

謝謝

+0

雖然未明確提及,但可能會引起您的興趣:['System.Dynamic.ExpandObject'](http://msdn.microsoft.com/zh-cn/library/system.dynamic.expandoobject%28v=vs。 110%29.aspx) – heltonbiker 2014-11-03 23:51:24

+0

'ExpandoObject'只是一個對象字典,可空結構也會被裝箱。 – IllidanS4 2014-11-04 00:01:19

回答

0

是的,這是可能的。

我已經做了一些研究,不管類型信息是否存儲在某處,我發現在設置成員的時候,有一個Func<System.Runtime.CompilerServices.CallSite,object,int?,object>對象被使用。這可能用於存儲綁定以備後用。

但是,這個緩存實際上被傳遞給活頁夾:private CallSiteBinder.Cache field。它包含一個​​,其中包含一個緩存委託類型作爲鍵和委託本身。因此,通過檢查委託類型的泛型參數,您可以獲得在賦值中使用的表達式的類型。

Complete方法:

private static readonly FieldInfo CallSiteBinder_Cache = typeof(CallSiteBinder).GetField("Cache", BindingFlags.NonPublic | BindingFlags.Instance); 
private static Type BindingType(CallSiteBinder binder) 
{ 
    IDictionary<Type,object> cache = (IDictionary<Type,object>)CallSiteBinder_Cache.GetValue(binder); 
    Type ftype = cache.Select(t => t.Key).FirstOrDefault(t => t != null && t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Func<,,,>)); 
    if(ftype == null) return null; 
    Type[] genargs = ftype.GetGenericArguments(); 
    return genargs[2]; 
} 

此方法獲得祕密緩存字典,並發現是已經從Func<T1,T2,T3,TResult>構造的類型鍵。然後只是提取類型參數。

+0

真棒,這個作品美味! – herps 2014-11-04 15:06:19

0

這是一個很好的問題,但沒有通用的解決方案。 object參數無法檢測通過的值是否先前存儲在Nullable<T>變量中。看起來你必須等到你有多個記錄,並看看是否有null和值的組合。

在這裏的具體情況,我會檢查 binder參數 ReturnType屬性,看看是否告訴你你需要什麼。 我看到你已經檢查過了。

您的一般問題是,您擁有的僅僅是值和類型,而不是表達式的靜態類型。例如,你不能區分這些情況之一:

string s = "hello"; 
object o = s; 
dynamo.P = s; // case 1 
dynamo.P = o; // case 2 

甚至

dynamo.Use(s); 
dynamo.Use(o); 

這是從靜態類型語言中,其中表達的類型中過載的使用非常不同解析度。它看起來像這樣的事情是不可能的DynamicObject

0

無法添加過載:

public override bool TrySetMember<T>(SetMemberBinder binder, Nullable<T> value) 
{ 
    properties[binder.Name] = value; 
    propertyTypes[binder.Name] = typeof(Nullable<T>); 
    return true; 
} 
+0

除非有基類方法覆蓋,否則不能覆蓋。請注意,在提供的代碼中,沒有對'TrySetMember'的顯式調用,只有編譯器合成的。 – 2014-11-03 23:20:38

+0

我的意思是MyExpando.TrySetMember上的重載(不是ExpandoObject.TrySetMember上的覆蓋)... – 2014-11-03 23:36:51

+0

您的評論與代碼中的「override」關鍵字不同。 – 2014-11-03 23:38:42