0

I have a scenario where I need the properties in my class to map to a dictionary. Here is a code sample:

public string Foo
{
    get
    {
        if (!PropertyBag.ContainsKey("Foo"))
        {
            return null;
        }

        return PropertyBag["Foo"];
    }
    set
    {
        PropertyBag["Foo"] = value;
    }
}

I have to apply this pattern to multiple properties. Is there a way to use attributes to do that?

I know that PostSharp would work for this purpose, but I was hoping there is a way to do it without using it.

user472875
  • 3,115
  • 4
  • 37
  • 68

4 Answers4

1

This feels like a code smell to me. It would be better to use regular POCOs and convert them to a Dictionary only when needed.

public class BlogPost
{
    public string Title { get; set; }
    public string Body { get; set; }
    public int AuthorId { get; set; }

    public Dictionary<string, object> ToDictionary()
    {
        return this.GetType()
            .GetProperties(BindingFlags.Instance | BindingFlags.Public)
            .ToDictionary(prop => prop.Name, prop => prop.GetValue(this, null));
    }
}

Inspiration: How to convert class into Dictionary?

And to be honest, a ToDictionary method on your POCO's seems like a code smell. It would be better to refactor your code so the conversion of POCOs to Dictionaries happens in its own layer, as a service maybe.

Edit: This Gist I found while searching google for "c# convert object to dictionary" could provide a more generalized solution, and probably more bullet proof than my cobbled together example:

Gist: https://gist.github.com/jarrettmeyer/798667

From the Gist:

public static class ObjectToDictionaryHelper
{
    public static IDictionary<string, object> ToDictionary(this object source)
    {
        return source.ToDictionary<object>();
    }

    public static IDictionary<string, T> ToDictionary<T>(this object source)
    {
        if (source == null)
            ThrowExceptionWhenSourceArgumentIsNull();

        var dictionary = new Dictionary<string, T>();
        foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(source))
            AddPropertyToDictionary<T>(property, source, dictionary);
        return dictionary;
    }

    private static void AddPropertyToDictionary<T>(PropertyDescriptor property, object source, Dictionary<string, T> dictionary)
    {
        object value = property.GetValue(source);
        if (IsOfType<T>(value))
            dictionary.add(property.Name, (T)value);
    }

    private static bool IsOfType<T>(object value)
    {
        return value is T;
    }

    private static void ThrowExceptionWhenSourceArgumentIsNull()
    {
        throw new ArgumentNullException("source", "Unable to convert object to a dictionary. The source object is null.");
    }
}

Credit: jerrettmeyer at GitHub

This should add a ToDictionary method to every object.

Edit: From the following comment

To give a bit of context, I am using Entity Framework and I have a class hierarchy that I would like to keep in one table while avoiding null columns everywhere.

Entity framework supports multiple table inheritance. That might be a better solution in your case.

Community
  • 1
  • 1
Greg Burghardt
  • 17,900
  • 9
  • 49
  • 92
  • To give a bit of context, I am using Entity Framework and I have a class hierarchy that I would like to keep in one table while avoiding null columns everywhere. – user472875 Aug 06 '14 at 18:09
  • All of these properties are marked as [NotMapped] in the code, and only the dictionary gets serialized. That being said, this solution is perfect. I need to do the reverse operation when deserializing but it eliminates all of the extra code. – user472875 Aug 06 '14 at 18:10
  • That was the initial solution I used. I found it unmanageable in my scenario. These objects represent Tasks that I need to be able to monitor all at once. The Multiple Table Inheritance approach creates n tables that I have to join on, plus an extra discriminator column that I have to add myself to figure out which type of task I am looking at. In addition, this collection of Task classes is increasing rapidly. What do you think? – user472875 Aug 06 '14 at 18:18
  • A case example that I looked it at is the scenario where a developer quickly wants to code up a new Task. In this scenario, the properties in the task are likely to change often as the coding progresses, which means that every such change would require a DB migration script. – user472875 Aug 06 '14 at 18:23
  • It actually sounds like you need a Task POCO and table, then create a join table between tasks and task types. Inheritance is an anti pattern here. You have a generic task, that can be flagged as one or more types. – Greg Burghardt Aug 06 '14 at 18:46
0

You can write a GetValueOrDefault extension method and reduce the code a little.

public static class DictionaryExtensions
{
    public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey,TValue> self, TKey key)
    {
        TValue value;
        self.TryGetValue(key,out value);
        return value;
    }
}

public string Foo
{
    get
    {    
        return PropertyBag.GetValueOrDefault("Foo");
    }
    set
    {
        PropertyBag["Foo"] = value;
    }
}

You can eliminate the magic strings using expressions.

Community
  • 1
  • 1
Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
0

If you're using at least .NET 4.5 then you have the CallerMemberNameAttribute which you could use like this:

class SomeClass
{
    public string Foo
    {
        get
        {
            return GetPropertyValue();
        }
        set
        {
            SetPropertyValue( value );
        }
    }

    private string GetPropertyValue( [CallerMemberName] string name = null )
    {
        string value;
        PropertyBag.TryGetValue( name, out value );
        return value;
    }

    private void SetPropertyValue( string value, [CallerMemberName] string name = null )
    {
        PropertyBag[name] = value;
    }
}

This will result in the compiler filling out the name of the member for you. If you're not (or otherwise can't) use .NET 4.5, another alternative would be to take advantage of expression trees as suggested in another answer.

Kyle
  • 6,500
  • 2
  • 31
  • 41
-1
class Test
{
    Dictionary<string,object> _values = new Dictionary<string, object>();

    public string Foo
    {
        get
        {
            var value = GetValue();
            return value == null ? string.Empty : (string)value;
        }

        set
        {
            SetValue(value);
        }
    }

    private object GetValue()
    {
        var stack = new StackTrace();
        var key = GetGenericName(stack.GetFrame(1).GetMethod().Name);
        if (_values.ContainsKey(key)) return _values[key];
        return null;
    }

    private void SetValue(object value)
    {
        var stack = new StackTrace();
        var key = GetGenericName(stack.GetFrame(1).GetMethod().Name);
        _values[key] = value;
    }

    private string GetGenericName(string key)
    {
        return key.Split('_')[1];
    }
}
Rob Epstein
  • 1,450
  • 9
  • 11
  • Yeah, this definitely helps get rid of those magic strings everywhere. Looks like it might be the best option in this case. – user472875 Aug 06 '14 at 17:53
  • 1
    Don't do that - using `StackTrace` in code that has nothing to do with debugging is wrong ([look at this question for more info](http://stackoverflow.com/q/2734596/335858)). – Sergey Kalinichenko Aug 06 '14 at 17:56
  • I agree that it makes for a fragile solution, but meets the requirement. The only other alternative is what the poster already provided. – Rob Epstein Aug 06 '14 at 18:13