-1

I'm trying to collate the properties from an existing object that contains some static properties. The object also contains a Dictionary with key value pairs. These key value pairs should be dynamically added as properties to the new object I'm creating.

This is the set up for source object:

public class MyClass
{
    public string PropA { get; set; }
    public string PropB { get; set; }
    public string PropC { get; set; }

    public IDictionary<string, NameValuePair> PropD { get; set; }
}

public class NameValuePair
{
    public string Name { get; set; }
    public string Value { get; set; }
}

This is where I create a list of the objects:

void Main()
{

    var data = new List<MyClass>
    {
        new MyClass {PropA = "A1", PropB = "B1", PropC = "C1", PropD = new Dictionary<string, NameValuePair>
        {
            { "Code11", new NameValuePair {Name = "PropD11", Value = "V11"}},
            { "Code12", new NameValuePair {Name = "PropD12", Value = "V12"}}
        }},
        new MyClass {PropA = "A2", PropB = "B2", PropC = "C2", PropD = new Dictionary<string, NameValuePair>
        {
            { "Code21", new NameValuePair {Name = "PropD21", Value = "V21"}},
            { "Code22", new NameValuePair {Name = "PropD22", Value = "V22"}},
        }},
        new MyClass {PropA = "A3", PropB = "B3", PropC = "C3", PropD = new Dictionary<string, NameValuePair>
        {
            { "Code12", new NameValuePair {Name = "PropD12", Value = "V31"}},
            { "Code21", new NameValuePair {Name = "PropD21", Value = "V32"}},
        }},
    };
//Extract column names from static properties
    var staticColumns = typeof(MyClass).GetProperties().Where(n => n.PropertyType == typeof(string)).Select(p => p.Name);

//Extract column names from the dictionary
    var dynamicColumns = data.Where(c => c.PropD.Any()).Select(c => c.PropD).SelectMany(c => c.Values.Select(v => v.Name)).Distinct().ToList();

//Combine to get a new list of columns 
    var columns = staticColumns.Union(dynamicColumns);

//Create object using columns

//Copy values from data to construct the new object.    
}

I need help with logic to construct an object at runtime, that has the structure as below and the data correctly mapped.

PropA    PropB    PropC  PropD11  PropD12  PropD21  PropD22
------------------------------------------------------------
A1       B1       C1     V11      V12       
A2       B2       C2                       V21      V22
A3       B3       C3              V31      V32  
user1145404
  • 285
  • 1
  • 3
  • 9
  • What are you asking for help with? – 15ee8f99-57ff-4f92-890c-b56153 Jun 04 '19 at 19:29
  • How do I add the properties, PropD11, PropD12, PropD21 and PropD22 dynamically so that the dynamic object looks like the table? – user1145404 Jun 04 '19 at 19:30
  • If you want to do that with an `ExpandoObject`, cast it to `IDictionary` – 15ee8f99-57ff-4f92-890c-b56153 Jun 04 '19 at 19:31
  • 1
    I don't know what a "dynamic object" means specifically here. If you are referring to `ExpandoObject` referenced as a `dynamic`, that is the wrong fit here, since your property names are held as runtime string and not compile-time symbols. It would be absurd to use an ExpandoObject for that. Why not just use a Dictionary or a DataTable? – John Wu Jun 04 '19 at 19:33
  • My data is already in another object List. By dynamic, I meant, creating another object at runtime, that transforms into an object with a structure as depicted in the table. – user1145404 Jun 04 '19 at 19:43
  • Do you know the `PropD`* property names at compile time? Why do you need `dynamic`? Why not create a class with those properties, and assign them from the `Dictionary` `PropD`? – NetMage Jun 04 '19 at 20:10
  • What does "transforms into an object" mean? – 15ee8f99-57ff-4f92-890c-b56153 Jun 04 '19 at 20:12
  • @EdPlunkett, 'transforms into another object' means: With fields as below: PropA PropB PropC PropD11 PropD12 PropD21 PropD22 I will not know the PropDnn properties at compile time. Business logic gathers data and adds to the dictionary. I'll have to dynamically add the Name in NameValuePair as property and Value in the NameValuePair as it's data. – user1145404 Jun 04 '19 at 20:47
  • @NetMage, I don't know PropD at compile time. PropD is a Dictionary that's constructed as part of some business logic. The result is that it has different key-value pairs. I need to expand these into individual properties. I could have another MyClass instance in the list like this: ``` new MyClass { PropA = "A3", PropB = "B3", PropC = "C3", PropD = new Dictionary { {"Code52", new NameValuePair {Name = "PropD52", Value = "V52"}} } }, ``` Then, I'll have to add another property with name = PropD52 and it's value V52. – user1145404 Jun 04 '19 at 20:55
  • Then what good does a dynamic or `ExpandoObject` do you? Those are for properties you know at compile time. – NetMage Jun 04 '19 at 21:38
  • @NetMage, I've not asked anything specifically about using dynamic or ExpandoObject...I'm using the word dynamic in literal sense, where, I construct my object at runtime 'dynamically'. – user1145404 Jun 04 '19 at 21:42
  • 1
    @user1145404 Ah, but that is exactly what you have in `MyClass` and the exact purpose of a `Dictionary`. This begins to sound like an [XY Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). What are you really trying to accomplish? What are the limitations of using `MyClass` to do that? – NetMage Jun 04 '19 at 21:43
  • @NetMage, MyClass has 4 properties, PropA, PropB, PropC and PropD. PropD is a Dictionary which can have various key value pairs. These key value pairs need to be added as separate properties. If you observe the table in my question, it is constructed using the data=List. I want an object built at runtime using data. The question is how to build it? I can extract the columns, but I need to create a new object with those columns and map the data. – user1145404 Jun 04 '19 at 21:52
  • 1
    _**Why**_ do they need to be separate properties? What does that gain you over just accessing them in `PropD`? Or perhaps changing `PropD` to be `Dictionary` based on each `NameValue` and ignoring the Codes? – NetMage Jun 04 '19 at 21:55
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/194464/discussion-between-user1145404-and-netmage). – user1145404 Jun 04 '19 at 22:14
  • @user1145404 That’s a dictionary. I think you’ve already solved this problem very well. If you (wisely) don’t care about ExpandoObject or dynamic, you’re set. – 15ee8f99-57ff-4f92-890c-b56153 Jun 04 '19 at 23:26

1 Answers1

1

Assuming your possible property names are static, here is an answer class:

public class AnsClass {
    public string PropA { get; set; }
    public string PropB { get; set; }
    public string PropC { get; set; }

    public string PropD11 { get; set; }
    public string PropD12 { get; set; }
    public string PropD21 { get; set; }
    public string PropD22 { get; set; }
}

And here is a LINQ statement to create a list of them:

var ans = data.Select(d => new AnsClass {
                    PropA = d.PropA,
                    PropB = d.PropB,
                    PropC = d.PropC,
                    PropD11 = d.PropD.Values.FirstOrDefault(v => v.Name == "PropD11")?.Value,
                    PropD12 = d.PropD.Values.FirstOrDefault(v => v.Name == "PropD12")?.Value,
                    PropD21 = d.PropD.Values.FirstOrDefault(v => v.Name == "PropD21")?.Value,
                    PropD22 = d.PropD.Values.FirstOrDefault(v => v.Name == "PropD22")?.Value,
                })
                .ToList();

If the members of PropD are dynamic, you can use the following to create ExpandoObjects but you are really better off just using MyClass the way it is - it has the best design for this already.

var ans2 = new List<dynamic>();
foreach (var d in data) {
    dynamic eo = new ExpandoObject();
    eo.PropA = d.PropA;
    eo.PropB = d.PropB;
    eo.PropC = d.PropC;
    var deo = (IDictionary<string,object>)eo;
    foreach (var dp in d.PropD.Values)
        deo.Add(dp.Name, dp.Value);
    ans2.Add(eo);
}
NetMage
  • 26,163
  • 3
  • 34
  • 55