Configuration object from name value pairs

753 Views Asked by At

I am working on a web app that needs to store and update configurations in a database. For example, I might store user preferences like default colors, languages, preferred date formats, etc.

The database table consists of name/value pairs (nvarchars). Simple enough.

I am using an ORM to fetch a list -- actually an IEnumerable -- of these pairs of name/value strings. My problem is that I need to turn these IEnumerable into something that is more expressive than strings of names/values. That is, I want to make a strongly-typed configuration object.

Ideally, I'd like to make it so that this configuration object doesn't have a direct dependency on the ORM, but I'm not sure how to go about this, since the initialization of this object and the persistence of the user preferences needs to go through the ORM.

Some sort of design pattern to the rescue?

(It shouldn't matter, but I'm using C# and Entity Framework.)


EDIT:

Let's say that I have the following fields in my domain objects:

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

public class GlobalSettings {
    public System.Drawing.Color FontColor {get;set;}
    public TimeZoneInfo DefaultTimeZone {get;set;}
    public DateTime DateCreated {get;set;}

    ... constructor ...
    ... validation, other functions ...
}

I can set up my ORM to fetch and persist SettingNameValuePair without much effort. However, how do I fetch and persist GlobalSettings? That is, somewhere in my application I want to get an instance of my Global Settings.

using (var context = new ApplicationContext()) {    
    GlobalSettings settings = ???
}

The ORM will not know how automatically convert between SettingNameValuePair and GlobalSettings, of course. I know I will need to write some "plumbing" code to produce the strongly-typed properties of GlobalSettings.

My question is: What is a good way to get back an instance of GlobalSettings that is populated by SettingNameValuePair? Here are two options are surely wrong:

1) Have the constructor of GlobalSettings take the data context as an argument. This introduces a bad dependency and is difficult to test.

2) Have the constructor of GlobalSettings take the SettingNameValuePair's from the data context like so:

using (var context = new ApplicationContext()) {    
    IEnumerable<SettingNameValuePair> nameValuePairs = 
        context.NameValuePairs.ToList();
    GlobalSettings settings = new GlobalSettings(nameValuePairs);
}

The problem with this is that you have to repeat this code all over the place.

I get the feeling that there are better solutions that involve Singletons, static classes, Proxies or Factory methods, but I'm not comfortable with these concepts yet.

2

There are 2 best solutions below

2
On BEST ANSWER

Don't reference your ORM in your domain object, instead implement it in the repository which will utilize either your ORM or whatever data access techniques (data reader for example as most orms will not be able to map between key value pair and strongly typed objects).

also i dont recommend to give the entity (globalsettings) a key value pair in the constructor as it is not related to domain do map key value pairs to domain object, instead implement the mapping in the mapper layer which is your repository or dal

for example:

In application layer

public GlobalSettings GetUserSettings(User user)
{
  return _globalSettingsRepository.GetGlobalSettings(user);
}

In repository layer

public GlobalSettings GetGlobalSettings(User user)
{
  var keyvalue = _context.blablabla();
  var globalSettings = new GlobalSettings { Id = keyvalue["key"], DateCreated = DateTime.Parse(keyvalue["datecreated"]) };
  return globalSettings
}
1
On

I don't think that there is any ORM out there that maps to Key-Value Pairs. In fact, it would probably be more efficient to use a DataReader for this data. However you get the data, you could simply use your configuration KVPs to build a dictionary, then use the Property Pattern for access. You can then create strongly typed accessors for the properties that you know that you will need. This will allow you to use strongly typed objects for the properties you know about, while still maintaining the flexibility to add properties without changing the code.