3

I'm trying to create some dynamic ExpandoObject. I've encountered a certain problem.

As I don't know what the name of these different properties in my objects should be, I can't do like this:

var list = new ArrayList();

var obj = new ExpandoObject();
obj.ID = 1,
obj.Product = "Pie",
obj.Days = 1,
obj.QTY = 65

list.Add(obj);

Let me explain my situation: I wish to get data from a random DB (I don't know which, but building a connection string from the information I get from the UI), therefore I don't know what data I need to get. This could be an example of a DB table

TABLE Sale

  • ID: int,
  • Product: nvarchar(100),
  • Days: int,
  • QTY: bigint

This could be another exmaple:

TABLE Foobar

  • Id: int,
  • Days: int
  • QTY: bigint
  • Product_Id: int
  • Department_Id: int

As you see, I don't know what the DB looks like (this is 100% anonymous, therefore it needs to be 100% dynamic), and the data I want to return should look like a well constructed JSON, like so:

[
  {
    "ID": 1,
    "Product": "Pie"
    "Days": 1,
    "QTY": 65
  },
  {
    "ID": 2,
    "Product": "Melons"
    "Days": 5,
    "QTY": 12
  }
]

Or, with the other example:

[
  {
    "ID": 1,
    "Days": 2,
    "QTY": 56,
    "Product_Id": 5,
    "Department_Id": 2
  }
  {
    "ID": 2,
    "Days": 6,
    "QTY": 12,
    "Product_Id": 2,
    "Department_Id": 5
  }
]

I've tried working with these ExpandoObjects, but can't seem to make it work, as I can't do what's illustrated in the top of this question (I don't know the names of the properties). Is there a way for me to say something like:

var obj = new ExpandoObject();
var propName = "Product";

var obj.propName = "Pie"

Console.WriteLine("Let's print!: " + obj.Product);

//OUTPUT
Let's print!: Pie

Does anyone have a solution, og simply guidance to a structure, that might solve this situation?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Detilium
  • 2,868
  • 9
  • 30
  • 65
  • what language are you using? you tagged C# but some of your code is Java (i.e System.out.printline) - .NET does not have this. – Ahmed ilyas Sep 16 '15 at 08:16
  • I didn't copy paste any code. I just can't remember what it's called in C#. This is pure C# and nothing else. My apologies. Will correct – Detilium Sep 16 '15 at 08:18
  • possible duplicate of [C# setting/getting the class properties by string name](http://stackoverflow.com/questions/10283206/c-sharp-setting-getting-the-class-properties-by-string-name) – GSerg Sep 16 '15 at 08:19
  • I would stay away from the ExpandoObject and do it with Reflection. This way you can create whole Classes at Runtime by getting the propertynames from the DB-Reader itself. – KarmaEDV Sep 16 '15 at 08:20
  • @KarmaEDV Reflection? Never worked with it? Could you guide me towards a link expaining what this is? – Detilium Sep 16 '15 at 08:20
  • Or just use a dictionary. You can serialize that to a json object too. – GSerg Sep 16 '15 at 08:21
  • 1
    Just create a `List>`. If you serialize that to JSON with, say, [Json.NET](http://www.newtonsoft.com/json), you will get what you want. – dbc Sep 16 '15 at 08:21
  • 2
    @GSerg Which `ExpandoObject` is :) – Patrick Hofman Sep 16 '15 at 08:22

5 Answers5

9

Rather than creating an ExpandoObject or some other dynamic type, you could create a List<Dictionary<string, object>> where each Dictionary<string, object> contains the name/value pairs you want to serialize. Then serialize to JSON using Json.NET (or JavaScriptSerializer, though that is less flexible):

        var list = new List<Dictionary<string, object>>();

        // Build a dictionary entry using a dictionary initializer: https://msdn.microsoft.com/en-us/library/bb531208.aspx
        list.Add(new Dictionary<string, object> { { "ID", 1 }, {"Product", "Pie"}, {"Days", 1}, {"QTY", 65} });

        // Build a dictionary entry incrementally
        // See https://msdn.microsoft.com/en-us/library/xfhwa508%28v=vs.110%29.aspx
        var dict = new Dictionary<string, object>();
        dict["ID"] = 2;
        dict["Product"] = "Melons";
        dict["Days"] = 5;
        dict["QTY"] = 12;
        list.Add(dict);

        Console.WriteLine(JsonConvert.SerializeObject(list, Formatting.Indented));
        Console.WriteLine(new JavaScriptSerializer().Serialize(list));

The first outputs:

[
  {
    "ID": 1,
    "Product": "Pie",
    "Days": 1,
    "QTY": 65
  },
  {
    "ID": 2,
    "Product": "Melons",
    "Days": 5,
    "QTY": 12
  }
]

The second outputs the same without the indentation:

[{"ID":1,"Product":"Pie","Days":1,"QTY":65},{"ID":2,"Product":"Melons","Days":5,"QTY":12}]
dbc
  • 104,963
  • 20
  • 228
  • 340
  • Won't really work in my case as I don't have all values available, but I need to loop through them. Could you give an example? `var valueList = allRowValues` and `var clumn = allClumnNames` (Working on an edit of main question) – Detilium Sep 16 '15 at 08:45
  • Dictionaries would be pretty useless if you couldn't [add](https://msdn.microsoft.com/en-us/library/k7z0zy8k%28v=vs.110%29.aspx) data after you created them @Detilium. – GSerg Sep 16 '15 at 08:49
  • 1
    @dbc for some reason my Json looks like this: `[{\"ID\":1,\"Product\":\"Honning\",\"Days\":1,\"QTY\":65}]` – Detilium Sep 16 '15 at 09:03
  • [No it does not look like this](http://stackoverflow.com/q/5465923/11683) @Detilium. – GSerg Sep 16 '15 at 09:05
  • @Detilium - you're probably looking at it in Visual Studio which is "helpfully" escaping the quotes in the string. To see it without quotes, type `Debug.WriteLine(myString)` in the [immediate window](https://msdn.microsoft.com/en-us/library/f177hahy.aspx). Or print it to the console. Also, answer updated with an example of how to build a dictionary incrementally. – dbc Sep 16 '15 at 09:06
  • [Yes it does actually look like this](http://imgur.com/CsnBve7) as I'm using Postman (google Extension) as I mentioned in the main question (not using the console in VS) Postman shows the raw output I get. It doesn't care about VS. If \ is in the string Postman shows the \ – Detilium Sep 16 '15 at 09:10
  • 1
    @Detilium - There's no mention of Postman anywhere in this question, so I'm not sure what that's about -- your question has `Console.WriteLine()`. But if you're posting this somehow, you may be [double-serializing your JSON](http://stackoverflow.com/questions/20013632/creating-a-double-serialized-json-object-in-rest-soe-c-sharp) somehow. – dbc Sep 16 '15 at 09:14
  • 2
    @dbc I did double serialize. Everything works now. Thank you for spending time in here. I really appreciate it – Detilium Sep 16 '15 at 09:19
1

As you can see here ExpandoObject Class, the ExpandoObject is implementing IDictionary<string, object>, so you can use that fact like

IDictionary<string, object> obj = new ExpandoObject();
var propName = "Product";
obj[propName] = "Pie"
Console.WriteLine("Let's print!: " + obj[propName]);
// Verify it's working
Console.WriteLine("Let's print again!: " + ((dynamic)obj).Product);
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
1

Use dynamic, then cast to IDictionary<string, object> to loop through your properties:

dynamic obj = new ExpandoObject();
obj.Product = "Pie";
obj.Quantity = 2;

// Loop through all added properties       
foreach(var prop in (IDictionary<string, object>)obj)
{
  Console.WriteLine(prop.Key + " : " + prop.Value);
}

I've made a fiddle: https://dotnetfiddle.net/yFLy2u

Now this is a solution to your question... other answers like @dbc's might be better suited to the problem (which is not the question, really)

Jcl
  • 27,696
  • 5
  • 61
  • 92
1

While I was writing the answer, I see you already got proper answer. You can use a Dictionary<string, onject> or even Tuple.

But as per your original question, you wanted to add properties dynamically. For that you can refer to other answer using ExpandoObject. This is just the same solution (using ExpandoObject to dynamically add properties) with classes similar to your code.

//example classes
public class DictKey
{
    public string DisplayName { get; set; }
    public DictKey(string name) { DisplayName = name; }
}

public class DictValue
{
    public int ColumnIndex { get; set; }
    public DictValue(int idx) { ColumnIndex = idx; }
}

//utility method
public static IDictionary<string, object> GetExpando(KeyValuePair<DictKey, List<DictValue>> dictPair)
{
    IDictionary<string, object> dynamicObject = new ExpandoObject();
    dynamicObject["Station"] = dictPair.Key.DisplayName;
    foreach (var item in dictPair.Value)
    {
        dynamicObject["Month" + (item.ColumnIndex + 1)] = item;
    }
    return dynamicObject;
}

Ans usage example:

var dictionaryByMonth = new Dictionary<DictKey, List<DictValue>>();
dictionaryByMonth.Add(new DictKey("Set1"), new List<DictValue> { new DictValue(0), new DictValue(2), new DictValue(4), new DictValue(6), new DictValue(8) });
dictionaryByMonth.Add(new DictKey("Set2"), new List<DictValue> { new DictValue(1), new DictValue(2), new DictValue(5), new DictValue(6), new DictValue(11) });

var rowsByMonth = dictionaryByMonth.Select(item => GetExpando(item));
Arghya C
  • 9,805
  • 2
  • 47
  • 66
0

First part, read this blog post by C# team thoroughly.

Lets see your code

var obj = new ExpandoObject();
var propName = "Product";

var obj.propName = "Pie"

Console.WriteLine("Let's print!: " + obj.Product);

//OUTPUT
Let's print!: Pie

In your code you are using var obj = new ExpandoObject();, so you are creating a statically typed object of type ExpandableObject. In the blog they specifically call out

I didn’t write ExpandoObject contact = new ExpandoObject(), because if I did contact would be a statically-typed object of the ExpandoObject type. And of course, statically-typed variables cannot add members at run time. So I used the new dynamic keyword instead of a type declaration, and since ExpandoObject supports dynamic operations, the code works

So if you rewrite your code to use dynamic obj, and add the dynamic properties as properties it should work!

But for your particular use case you better use Dictionaries as suggested above by @dbc

dynamic obj = new ExpandoObject();
obj.Product= "Pie"    
Console.WriteLine("Let's print!: " + obj.Product);    
//OUTPUT
Let's print!: Pie
Adarsha
  • 2,267
  • 22
  • 29
  • Neither his nor your code will work. They will not even compile. When you get them to compile by removing an unwanted `var` and adding a missing `;`, you will see it does not do what you expect and throws an exception instead of printing "Pie", because this code adds property `propName` to `obj`, not property `Product`. – GSerg Sep 16 '15 at 09:02
  • c**p, i noticed only the static type as the problem. will edit my answer to fix them too. I don't have access to Visual studio at the moment, came to SO for something else, saw this! – Adarsha Sep 16 '15 at 09:07