13

I have a class MyClass. I would like to convert this to a dynamic object so I can add a property.

This is what I had hoped for:

dynamic dto = Factory.Create(id);
dto.newProperty = "123";

I get the error:

WEB.Models.MyClass does not contain a definition for 'newProperty'

Is that not possible?

Sean
  • 14,359
  • 13
  • 74
  • 124
  • 8
    dynamic does not mean you can add properties on the go. It means you don't know what it will be so you use dynamic (as far as I know) and the type will be figured out at runtime. – EpicKip Mar 16 '17 at 14:30
  • 2
    You need to use [ExpandoObject](https://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject(v=vs.110).aspx) if you want to add properties. Here is a [link](http://stackoverflow.com/questions/3540161/c-sharp-4-0-dynamic-vs-expando-where-do-they-fit) with some info that might help. – MaKCbIMKo Mar 16 '17 at 14:33

5 Answers5

17

The following has worked for me in the past:
It allows you to convert any object to an Expando object.

public static dynamic ToDynamic<T>(this T obj)
{
    IDictionary<string, object> expando = new ExpandoObject();

    foreach (var propertyInfo in typeof(T).GetProperties())
    {
        var currentValue = propertyInfo.GetValue(obj);
        expando.Add(propertyInfo.Name, currentValue);
    }
    return expando as ExpandoObject;
}

Based on: http://geekswithblogs.net/Nettuce/archive/2012/06/02/convert-dynamic-to-type-and-convert-type-to-dynamic.aspx

StfBln
  • 1,137
  • 6
  • 11
  • This is close to what I want, but I get an error: Expression of type 'System.Guid' cannot be used for return type 'System.String' – Sean Mar 16 '17 at 14:40
  • What advantage does the expression tree have over `expando.Add(propertyInfo.Name, propertyInfo.GetValue(obj));`? And why does it convert property names to lowercase? And why does it cast an `ExpandoObject` to `ExpandoObject` when returning? – chris Mar 16 '17 at 14:40
  • @chris I used it for a specific case. For this post, it is indeed not required. Please see updated answer. – StfBln Mar 16 '17 at 14:44
  • @Sean, I'm pretty sure that error came from treating all properties as being strings when it formed the `Lambda>` instead of using the actual type of the property as the return type. I'm not sure how one would actually do that properly. – chris Mar 16 '17 at 14:48
4

As my object has JSON specific naming, I came up with this as an alternative:

public static dynamic ToDynamic(this object obj)
{
  var json = JsonConvert.SerializeObject(obj);
  return JsonConvert.DeserializeObject(json, typeof(ExpandoObject));
}

For me the results worked great:

Model:

public partial class Settings
{
  [JsonProperty("id")]
  public int Id { get; set; }

  [JsonProperty("runTime")]
  public TimeSpan RunTime { get; set; }

  [JsonProperty("retryInterval")]
  public TimeSpan RetryInterval { get; set; }

  [JsonProperty("retryCutoffTime")]
  public TimeSpan RetryCutoffTime { get; set; }

  [JsonProperty("cjisUrl")]
  public string CjisUrl { get; set; }

  [JsonProperty("cjisUserName")]
  public string CjisUserName { get; set; }

  [JsonIgnore]
  public string CjisPassword { get; set; }

  [JsonProperty("importDirectory")]
  public string ImportDirectory { get; set; }

  [JsonProperty("exportDirectory")]
  public string ExportDirectory { get; set; }

  [JsonProperty("exportFilename")]
  public string ExportFilename { get; set; }

  [JsonProperty("jMShareDirectory")]
  public string JMShareDirectory { get; set; }

  [JsonIgnore]
  public string Database { get; set; }
}

I used it in this manner:

private static dynamic DynamicSettings(Settings settings)
{
  var settingsDyn = settings.ToDynamic();
  if (settingsDyn == null)
    return settings;
  settingsDyn.guid = Guid.NewGuid();
  return settingsDyn;
}

And received this as a result:

{
  "id": 1,
  "runTime": "07:00:00",
  "retryInterval": "00:05:00",
  "retryCutoffTime": "09:00:00",
  "cjisUrl": "xxxxxx",
  "cjisUserName": "xxxxx",
  "importDirectory": "import",
  "exportDirectory": "output",
  "exportFilename": "xxxx.xml",
  "jMShareDirectory": "xxxxxxxx",
  "guid": "210d936e-4b93-43dc-9866-4bbad4abd7e7"
}

I don't know about speed, I mean it is serializing and deserializing, but for my use it has been great. A lot of flexability like hiding properties with JsonIgnore.

Note: xxxxx above is redaction. :)

Rob10e
  • 319
  • 2
  • 9
2

You cannot add members to class instances on the fly.

But you can use ExpandoObject. Use factory to create new one and initialize it with properties which you have in MyClass:

public static ExpandoObject Create(int id)
{
    dynamic obj = new ExpandoObject();
    obj.Id = id;
    obj.CreatedAt = DateTime.Now;
    // etc
    return obj;
} 

Then you can add new members:

dynamic dto = Factory.Create(id);
dto.newProperty = "123";
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
0

You can't add properties to types at runtime. However, there is an exception which is: ExpandoObject. So if you need to add properties at runtime you should use ExpandoObject, other types don't support this.

Selman Genç
  • 100,147
  • 13
  • 119
  • 184
0

Just to add up my experience, if you are using JSON.NET, then below might be one of the solution:

var obj....//let obj any object
ExpandoObject expandoObject= JsonConvert.DeserializeObject<ExpandoObject>(JsonConvert.SerializeObject(obj));

Not tested performances etc.. but works.

codemirror
  • 3,164
  • 29
  • 42
StartCoding
  • 394
  • 5
  • 16