2013-05-15 15 views
7

Ho un ExpandoObject che sto inviando a un metodo di libreria esterno che accetta un oggetto. Da quello che ho visto questa lib esterna usa TypeDescriptor.GetProperties internamente e questo sembra causare alcuni problemi con il mio ExpandoObject.Esposizione delle proprietà di un ExpandoObject

Potrei andare con un oggetto anonimo e invece sembra funzionare ma per me è molto più comodo usare ExpandoObject.

Ho bisogno di costruire il mio oggetto DynamicObject e di prendermi cura personalmente implementando ICustomTypeDescriptor o mi manca qualcosa qui.

Idee?


Aggiornamento

Oltre alla risposta da somedave di seguito (come per i commenti), ho aggiunto questa classe

public class ExpandoObjectTypeDescriptionProvider : TypeDescriptionProvider 
{ 
    private static readonly TypeDescriptionProvider m_Default = TypeDescriptor.GetProvider(typeof(ExpandoObject)); 

    public ExpandoObjectTypeDescriptionProvider() 
     :base(m_Default) 
    { 
    } 

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) 
    { 
     var defaultDescriptor = base.GetTypeDescriptor(objectType, instance); 

     return instance == null ? defaultDescriptor : 
      new ExpandoObjectTypeDescriptor(instance); 
    } 
} 

e registrato in questo modo:

dynamic parameters = new ExpandoObject(); 
TypeDescriptor.AddProvider(new ExpandoObjectTypeDescriptionProvider(), parameters); 
+0

Avete un elenco dei nomi di proprietà sulla vostra estremità? –

+0

non in anticipo (tempo di compilazione) ... da qui l'ExpandoObject –

risposta

9

L'implementazione di ICustomTypeDescriptor non è poi così difficile. Ecco alcuni esempi di codice che ho adattato da un lavoro svolto con le griglie di proprietà di WinForms (che utilizza TypeDescriptor e PropertyDescriptor). Il trucco è anche quello di implementare una classe PropertyDescriptor appropriata che puoi passare da ICustomTypeDescriptor.GetProperties(). Fortunatamente ExpandoObject rende questo abbastanza semplice implementando IDictionary<string, object> per il recupero dinamico delle sue chiavi e valori. Tieni presente che questo potrebbe o non potrebbe funzionare correttamente (non l'ho testato) e probabilmente non funzionerà con ExpandoObjects con molte proprietà annidate.

public class ExpandoTypeDescriptor : ICustomTypeDescriptor 
{ 
    private readonly ExpandoObject _expando; 

    public ExpandoTypeDescriptor(ExpandoObject expando) 
    { 
     _expando = expando; 
    } 

    // Just use the default behavior from TypeDescriptor for most of these 
    // This might need some tweaking to work correctly for ExpandoObjects though... 

    public string GetComponentName() 
    { 
     return TypeDescriptor.GetComponentName(this, true); 
    } 

    public EventDescriptor GetDefaultEvent() 
    { 
     return TypeDescriptor.GetDefaultEvent(this, true); 
    } 

    public string GetClassName() 
    { 
     return TypeDescriptor.GetClassName(this, true); 
    } 

    public EventDescriptorCollection GetEvents(Attribute[] attributes) 
    { 
     return TypeDescriptor.GetEvents(this, attributes, true); 
    } 

    EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents() 
    { 
     return TypeDescriptor.GetEvents(this, true); 
    } 

    public TypeConverter GetConverter() 
    { 
     return TypeDescriptor.GetConverter(this, true); 
    } 

    public object GetPropertyOwner(PropertyDescriptor pd) 
    { 
     return _expando; 
    } 

    public AttributeCollection GetAttributes() 
    { 
     return TypeDescriptor.GetAttributes(this, true); 
    } 

    public object GetEditor(Type editorBaseType) 
    { 
     return TypeDescriptor.GetEditor(this, editorBaseType, true); 
    } 

    public PropertyDescriptor GetDefaultProperty() 
    { 
     return null; 
    } 

    // This is where the GetProperties() calls are 
    // Ignore the Attribute for now, if it's needed support will have to be implemented 
    // Should be enough for simple usage... 

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() 
    { 
     return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]); 
    } 

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes) 
    { 
     // This just casts the ExpandoObject to an IDictionary<string, object> to get the keys 
     return new PropertyDescriptorCollection(
      ((IDictionary<string, object>)_expando).Keys 
      .Select(x => new ExpandoPropertyDescriptor(((IDictionary<string, object>)_expando), x)) 
      .ToArray()); 
    } 

    // A nested PropertyDescriptor class that can get and set properties of the 
    // ExpandoObject dynamically at run time 
    private class ExpandoPropertyDescriptor : PropertyDescriptor 
    { 
     private readonly IDictionary<string, object> _expando; 
     private readonly string _name; 

     public ExpandoPropertyDescriptor(IDictionary<string, object> expando, string name) 
      : base(name, null) 
     { 
      _expando = expando; 
      _name = name; 
     } 

     public override Type PropertyType 
     { 
      get { return _expando[_name].GetType(); } 
     } 

     public override void SetValue(object component, object value) 
     { 
      _expando[_name] = value; 
     } 

     public override object GetValue(object component) 
     { 
      return _expando[_name]; 
     } 

     public override bool IsReadOnly 
     { 
      get 
      { 
       // You might be able to implement some better logic here 
       return false; 
      } 
     } 

     public override Type ComponentType 
     { 
      get { return null; } 
     } 

     public override bool CanResetValue(object component) 
     { 
      return false; 
     } 

     public override void ResetValue(object component) 
     { 
     } 

     public override bool ShouldSerializeValue(object component) 
     { 
      return false; 
     } 

     public override string Category 
     { 
      get { return string.Empty; } 
     } 

     public override string Description 
     { 
      get { return string.Empty; } 
     } 
    } 
} 
+0

È grandioso, ma in realtà sto inviando il mio ExpandoObject alla libreria esterna e ExpandoObj è sigillato quindi non posso ereditarlo. Quindi, come faccio a dire di usare la mia implementazione di ICustomTypeDescriptor quindi ...? –

+2

Ah, non ho capito completamente il problema. Penso che il trucco stia creando una classe addizionale, un'implementazione 'TypeDescriptionProvider'. Ha un metodo 'GetTypeDescriptor()' (con diversi overload) che restituisce un 'ICustomTypeDescriptor' per un dato oggetto o tipo. Rigate fino a restituire un'istanza di 'ExpandoTypeDescriptor' sopra. Quindi registrare il provider usando 'TypeDescriptor.AddProvider()'. Penso che dovrebbe funzionare tutto insieme. Ha senso? – daveaglick

+0

Contrassegnerò la tua risposta come corretta poiché ha funzionato con le cose che hai aggiunto nei commenti ... Modificherò la mia domanda per evidenziarlo. tnx !! –

Problemi correlati