5

I've got an object array of Key values.

public class KeyValueStore
{
   public string Key {get;set;}
   public string Value {get;set;}
}

This array stores the values of an object i am trying to fill like this:

public class Customer
{
   public string Name {get;set;}
   public string Country {get;set}
}

So i want to map these keys from KeyValueStore to Customer properties

public Customer TransformToCustomer(KeyValueStore[] keyValueStore)
{
    var customer = new Customer();

    foreach (var keyValue in keyValueStore)
    {
        switch (keyValue.Key)
        {
            case "Name":
                customer.Name = keyValue.Value;
                break;
            case "Cntry":
                customer.Country = keyValue.Value;
                break;
        }
    }

    return customer;
}

Is there a better way to do this?

GuacaMollee
  • 103
  • 1
  • 10
  • From a quick glance it's going to be difficult to avoid any kind of switch/lookup type of code since your Keys aren't event lining up to the properties. What I mean is `"Cntry" != Country` – Mark C. Sep 29 '16 at 15:09
  • Assuming that keys are actually property names, without typos, take a look here: http://stackoverflow.com/questions/7718792/can-i-set-a-property-value-with-reflection – kiziu Sep 29 '16 at 15:10
  • 1
    Any reason you are re-inventing the `Dictionary` here with your `KeyValueStore` class? – stephen.vakil Sep 29 '16 at 15:38
  • You guessed it right Mark C., I've added the 'Typo' to show this could happen as well. – GuacaMollee Sep 30 '16 at 07:44

2 Answers2

4

Yes, assuming that the destination type has a parameterless constructor, you could write a generic method that does this:

public T CreateAndPopulate<T>(IEnumerable<KeyValueStore> propStore,
                              IDictionary<string, string> mapping = null) 
                             where T:class,new()
{

    T item=new T();
    var type=typeof(T);
    foreach(var kvs in propStore)
    {
        var propName = kvs.Key;
        propName = mapping !=null && mapping.ContainsKey(propName) 
                       ? mapping[propName] 
                       : propName;
        var prop = type.GetProperty(propName);
        if(prop == null) //does the property exist?
        {
            continue;
        }
        var propMethodInfo = prop.GetSetMethod();
        if(propMethodInfo == null) //does it have a set method?
        {
            continue;
        }
        propMethodInfo.Invoke(item, new[]{ kvs.Value });
    }
    return item;
}

and use it:

IEnumerable<KeyValueStore> propStore = new KeyValueStore[]{
    new KeyValueStore{ Key = "Name", Value = "John" },
    new KeyValueStore{ Key = "Cntry", Value = "UK" }};
var mapping = new Dictionary<string,string>{{ "Cntry", "Country" }};

var customer = CreateAndPopulate<Customer>(propStore, mapping);
spender
  • 117,338
  • 33
  • 229
  • 351
1

I have another suggestion, a lot of times big switch blocks indicates you are missing something with your objects desigen, and proper use of polymorphism can replace the switch usage.

First we will redesign the KeyValueStore class to seprate ValueStore classes that each one will implement the common interface IValueStore, the interface will look something like this:

public interface IValueStore
{
    void AddValueToCostumer(Customer customer);
}

Now NameValueStore will look like this:

public class NameValueStore : IValueStore
{
    private readonly string _name;       

    public NameValueStore(string name)
    {
        _name = name;
    }

    public void AddValueToCustomer(Costumer costumer)
    {
        customer.Name = _name;
    }
}

And CountryValueStore:

public class CountryValueStore : IValueStore
{
    private readonly string _country;       

    public CountryNameValueStore(string country)
    {
        _country = country;
    }

    public void AddValueToCustomer(Costumer costumer)
    {
        customer.Country = _country;
    }
}

And now your function TransformToCustomer could look like this:

public Customer TransformToCustomer(IValueStore[] valueStores)
{
    var customer = new Customer();

    foreach (var valueStore in valueStores)
    {
        valueStore.AddValueToCustomer(customer);
    }

    return customer;
}

This solution feels much more SOLID to me.

Hope it helps!

YuvShap
  • 3,825
  • 2
  • 10
  • 24
  • This is not an option for me. my Customer class is actually really large. I have to make 50 classes for all the properties. But this could really be the solution for smaller classes. Thank you for you help though! :) – GuacaMollee Sep 30 '16 at 07:46
  • I agree that in your case create 50 classes for this purpose is not worth it, but in cases where each of the classes logic is more complex than yours it is sometimes worth the overhead of creating a lot of small classes to use the power of polymorphism. – YuvShap Sep 30 '16 at 08:22
  • And i agree on that as well. :) This answer was very helpful for future projects! – GuacaMollee Sep 30 '16 at 08:53