2014-10-17 3 views
0

Проблема

Использование C#, мне нужно пройти через объект, который был брошен на ExpandoObject из XML и заменить любое свойство «цена» с новым значением.В C#, как изменить значение Key-Value-Property при рекурсивном перемещении ExpandoObject?

Этот объект очень неструктурирован и имеет много слоев вложенных узлов (на самом деле это вложенные ExpandoObjects). Более конкретно, иерархия может выглядеть следующим образом:

продукта => цена, количество, аксессуары

Каждый аксессуар может иметь цену и количество, и само по себе может иметь вспомогательное оборудование, поэтому мне нужно рекурсии.

То, что я до сих пор

public ExpandoObject UpdatePricing(ExpandoObject exp) 
    { 
     //Is there a price property? 
     var hasPrice = exp.Any(a => a.Key == "price"); 
     if (hasPrice) 
     { 
      //update price here 
      exp.price = 0; //Some other price 
     } 

     //Now loop through the whole object. If any of the properties contain an expando, then call this method again 
     foreach (var kvp in exp) 
     { 
      if (kvp.Value is ExpandoObject) 
      { 
       //THIS CODE IS NO GOOD BECAUSE kvp.Value has no setter!! 
       kvp.Value = UpdatePricing(kvp.Value); 
      } 
     } 

     return exp; 
    } 

Проблема я бегу в том, что kvp.Value не сеттер, поэтому я не могу запустить этот метод рекурсивно.

Любые предложения приветствуются. Благодаря!

+0

Я бы реорганизовал структуру в дереве. – BlueTrin

+0

Какую ошибку вы получаете конкретно? Когда вы запускаете его, какое исключение возникает? – Haney

+0

Какой смысл возвращать kvp? Почему не просто рекурсивно обрабатывать метод void? Или просто не присваивайте результат kvp.Value. – codenheim

ответ

1

С ExpandoObject инвентарь IDictionary<string, Object> все может быть немного легче. Мы также можем изменить тип возврата на void, потому что нам не нужно переназначать результат.

void UpdatePrice(ExpandoObject expando, decimal price) 
{ 
    var map = (IDictionary<string, Object>)expando; 
    if (map.ContainsKey("price")) 
     map["price"] = price; 
    foreach (var value in map.Values) 
    { 
     if (value is ExpandoObject) 
      UpdatePrice((ExpandoObject)value, price); 
    } 
} 
1

Я мало знаю о ExpandoObject. Но, как и большинство реализаций словаря, я предполагаю, что если вы хотите, чтобы ваша пара ключей и значений была обновлена, чтобы иметь другое значение, вам нужно пройти через интерфейс словаря.

Обратите внимание, что вам (возможно) не будет разрешено изменять словарь при перечислении его содержимого. Поэтому вам нужно будет создать список элементов для обновления и сделать это в отдельной операции.

Например:

List<string> keysToUpdate = new List<string>(); 

foreach (var kvp in exp) 
{ 
    if (kvp.Value is ExpandoObject) 
    { 
     keysToUpdate.Add(kvp.Key); 
    } 
} 

foreach (string key in keysToUpdate) 
{ 
    exp[key] = UpdatePricing(exp[key]); 
} 

Вы также можете сохранить всю ценность KeyValuePair в списке, чтобы избежать второго извлечения значения, но я предполагаю, что это не важно, оптимизация здесь.

1

Я просто провел небольшой тест на это и был в состоянии получить его на работу за счет того, Expando быть динамичным:

public static ExpandoObject DoWork(ExpandoObject obj) 
    { 
     dynamic expando = obj; 

     //update price 
     if (obj.Any(c => c.Key == "price")) 
      expando.price = 354.11D; 

     foreach (var item in expando) 
     { 
      if (item.Value is ExpandoObject) 
      { 
       //call recursively 
       DoWork(item.Value); 
      } 
     } 
     return expando; 
    } 

это elimitates типобезопасность, но, похоже, у вас нет такой роскоши в любом случае, динамика лучший способ взаимодействия с expandos на самом деле в соответствии с MSDN:.

«в C#, чтобы включить позднее связывание для экземпляра класса ExpandoObject , вы должны использовать динамическое ключевое слово для получения дополнительной информации , см. Использование Тип динамический (Руководство по программированию на C#). "

это означает, что если вы не используете dynamic ключевое слово, вы запускаете EXPANDO в CLR вместо DLR, который будет иметь некоторые странные последствия, как не в состоянии установить значение. Надеюсь, это поможет.