2012-09-14 30 views
3

我使用反射來更新他們不得不他們所做的更新對象,並保存到MongoDB的C#反射的SetValue()無法找到set訪問

private void updateSelf(MongoDoc newDoc) 
    { 
     Type type = this.GetType(); 
     foreach (var i in type.GetProperties()) 
     { 
      if (i.GetCustomAttributes(false).Any(x => x is MongoDB.Bson.Serialization.Attributes.BsonIgnoreAttribute)) continue; 
      Object oldValue = i.GetValue(this, null); 
      Object newValue = i.GetValue(newDoc, null); 
      if (!Object.Equals(oldValue, newValue) && !((oldValue == null) && (newValue == null))) 
      { 
       i.SetValue(this, newValue, null); 
      } 
     } 
    } 

這是工作的大部分,但i.SetValue(this, newValue, null);拋出一個異常試圖更新該屬性時:

public uint Revision { get; private set; } 

此試圖更新Product類型的對象是這是造成異常0123的MongoDoc其包含屬性public uint Revision { get; private set; }派生類型我不確定是什麼導致了這個問題,因爲它適用於我所有的其他屬性,只是這個引發了異常。任何幫助非常讚賞

UPDATE:

我曾嘗試下面的答案:

i.SetValue(this, newValue, System.Reflection.BindingFlags.SetProperty | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic, null, null, null); 

但不幸的是完全相同的結果,但它仍然拋出的修訂財產除外。

UPDATE:

例外:

System.ArgumentException was unhandled 
    Message=Property set method not found. 
    Source=mscorlib 
    StackTrace: 
     at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture) 
     at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index) 
     at Flo.Client.Docs.MongoDoc.updateSelf(MongoDoc newDoc) in F:\Flo\Flo.Client\Docs\MongoDoc.cs:line 162 
     at Flo.Client.Docs.MongoDoc.UpdateToMongo(MongoDoc newDoc) in F:\Flo\Flo.Client\Docs\MongoDoc.cs:line 120 
     at Flo.Client.Docs.Product.EditProduct(String Name, Nullable`1 State) in F:\Flo\Flo.Client\Docs\Product.cs:line 89 
     at Flo.Client.Program.Main() in F:\Flo\Flo.Client\Program.cs:line 26 
     at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args) 
     at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args) 
     at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
     at System.Threading.ThreadHelper.ThreadStart_Context(Object state) 
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx) 
     at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
     at System.Threading.ThreadHelper.ThreadStart() 
    InnerException: 
+0

你的房屋都有'私人設置'訪問? –

+0

是的,這就是讓我困惑的原因「我想也許這是因爲屬性設置訪問器是私人的」但是我的其他屬性也有私有設置訪問器,並且它們會被寫入到很好的狀態,而且我讀了一些反射並不關心的地方關於財產訪問者的級別,只要財產本身是公開的,就可以得到它。 – 0xor1

+0

是'Product'類還是'MongoDoc'類的'Revision'屬性? –

回答

2

我這個固定,由於迪倫Meador指着我這給了我足夠得到解決另一個問題:

private void updateSelf(MongoDoc newDoc, Type type) 
    { 
     foreach (var i in type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public)) 
     { 
      if (i.GetCustomAttributes(false).Any(x => x is MongoDB.Bson.Serialization.Attributes.BsonIgnoreAttribute)) continue; 
      Object oldValue = i.GetValue(this, null); 
      Object newValue = i.GetValue(newDoc, null); 
      if (!Object.Equals(oldValue, newValue) && !((oldValue == null) && (newValue == null))) 
      { 
       i.SetValue(this, newValue, null); 
      } 
     } 
     Type baseType = type.BaseType; 
     if (baseType != null) 
     { 
      this.updateSelf(newDoc, baseType); 
     } 
    } 

看起來需要被明確設置爲基類的類型鍵入以便爲該特定屬性使用set訪問器。

+1

您必須使用基類型的原因是因爲set訪問器是私有的。用'class A {}; B類:A {}',B無權訪問A的私人成員,因此反思也不允許您通過B類訪問A的私人成員。 – phoog

+0

事實上,我的情況確實如此。我得到了DeclaringType的屬性,並能夠設置屬性。 var propertyInfo = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty).FirstOrDefault(); var p2 = propertyInfo.DeclaringType.GetProperty(propertyInfo.Name,BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty); p2.SetValue(instance,messageHandler,BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty,null,null,null); –

1

嘗試使用具有System.Reflection.BindingFlags參數的SetValue的過載,並通過它的BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.NonPublic的值。

+0

試着用'i.SetValue(this,newValue,System.Reflection.BindingFlags.SetProperty | System.Reflection.BindingFlags.Public | System。 Reflection.BindingFlags.NonPublic,null,null,null);'但無濟於事發生完全相同的問題 – 0xor1

0

問題是,從派生類型的角度來看,沒有屬性的setter。

您可以解決這個問題:遍歷繼承層次結構並獲取每種類型聲明的屬性(使用DeclaredOnly BindingFlag,與Public和Instance一起),或者檢查屬性是否由反射類型聲明,如果沒有得到它再次來自屬性信息的DeclaringType。

對於前者你,你可以有一個嵌套的循環,而後者則是這個樣子:

foreach (var property in p.GetType().GetProperties()) 
    { 
     var actualProperty = property.DeclaringType != property.ReflectedType ? property.DeclaringType.GetProperty(property.Name) : property; 
     actualProperty.SetValue(p, newValue, null); 
    } 
+0

爲什麼不直接使用property.DeclaringType? – phoog

+0

您的意思不是檢查它是否與property.ReflectedType不同?它們確實相當。 – fsimonazzi

+0

我的意思是爲什麼要使用條件操作符?說var'actualProperty = property.DeclaringType.GetProperty(property.Name);'而不是'var actualProperty = property.DeclaringType!= property.ReflectedType? property.DeclaringType.GetProperty(property.Name):property;'。 – phoog

0

也許問題能在明年:
您正在使用自動屬性
public uint Revision { get; private set; } 通過默認類型int執行裝箱/取消裝箱操作。 所以,在這裏您應該明確地轉換爲uint類型。

你可以用下一片段發現類似的問題:

byte b1 = 4; 
byte b2 = 5; 
byte sum = b1 + b2; 

這將引發異常,因爲沒有過載被宣佈爲「+」操作符。 這段代碼實際上通過默認類型int演示了裝箱/取消裝箱操作的問題。