Skip to content

Category: mvc

Strongly Typing TempData in your MVC Application with Extension Methods

As I’ve stated before, and for those that know me, when working with C# I try to use the compiler as often as I can and keep things strongly typed.  When I started working in MVC, I didn’t like the fact that TempData was defined like this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class TempDataDictionary : IDictionary<string, object>, ICollection<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>, IEnumerable
public class TempDataDictionary : IDictionary<string, object>, ICollection<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>, IEnumerable
public class TempDataDictionary : IDictionary<string, object>, ICollection<KeyValuePair<string, object>>, IEnumerable<KeyValuePair<string, object>>, IEnumerable

While TempData and ViewData being potentially valuable things, <string, object>, really?

Here is how I get around that and use the compiler to my advantage with some simple extension methods:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public static class TempDataExtensions
{
public static T Get<T>(this TempDataDictionary tempData, string key)
{
if (tempData[key] is T)
{
var tempDataItem = (T)tempData[key];
return tempDataItem;
}
throw new InvalidCastException(string.Format("Temp Data does not contain type {0} for key {1}", typeof(T), key));
}
public static void Set<T>(this TempDataDictionary tempData, string key, T value)
{
tempData[key] = value;
}
}
public static class TempDataExtensions { public static T Get<T>(this TempDataDictionary tempData, string key) { if (tempData[key] is T) { var tempDataItem = (T)tempData[key]; return tempDataItem; } throw new InvalidCastException(string.Format("Temp Data does not contain type {0} for key {1}", typeof(T), key)); } public static void Set<T>(this TempDataDictionary tempData, string key, T value) { tempData[key] = value; } }
public static class TempDataExtensions
{
    public static T Get<T>(this TempDataDictionary tempData, string key)
    {
        if (tempData[key] is T)
        {
            var tempDataItem = (T)tempData[key];
            return tempDataItem;
        }
        throw new InvalidCastException(string.Format("Temp Data does not contain type {0} for key {1}", typeof(T), key));
    }
 
    public static void Set<T>(this TempDataDictionary tempData, string key, T value)
    {
        tempData[key] = value;
    }
}

So, in your controller, you can Set to TempData and Get from TempData like this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public ActionResult Index()
{
TempData.Set("SomeObjectKey", new SomeObject());
TempData.Set("SomeBoolKey", true);
TempData.Set("SomeStringKey", "test");
TempData.Get<SomeObject>("SomeObjectKey"); // returns SomeObject
TempData.Get<bool>("SomeBoolKey"); // returns a boolean true
TempData.Get<string>("SomeStringKey"); // returns the string "test"
return View();
}
public ActionResult Index() { TempData.Set("SomeObjectKey", new SomeObject()); TempData.Set("SomeBoolKey", true); TempData.Set("SomeStringKey", "test"); TempData.Get<SomeObject>("SomeObjectKey"); // returns SomeObject TempData.Get<bool>("SomeBoolKey"); // returns a boolean true TempData.Get<string>("SomeStringKey"); // returns the string "test" return View(); }
public ActionResult Index()
{
    TempData.Set("SomeObjectKey", new SomeObject());
    TempData.Set("SomeBoolKey", true);
    TempData.Set("SomeStringKey", "test");
     
    TempData.Get<SomeObject>("SomeObjectKey"); // returns SomeObject
    TempData.Get<bool>("SomeBoolKey"); // returns a boolean true
    TempData.Get<string>("SomeStringKey"); // returns the string "test"
 
    return View();
}

You can also do the same with ViewData:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public static class ViewDataExtensions
{
public static T Get<T>(this ViewDataDictionary viewData, string key)
{
if (viewData[key] is T)
{
var viewDataItem = (T)viewData[key];
return viewDataItem;
}
throw new InvalidCastException(string.Format("View Data does not contain type {0} for key {1}", typeof(T), key));
}
public static void Set<T>(this ViewDataDictionary viewData, string key, T value)
{
viewData[key] = value;
}
}
public static class ViewDataExtensions { public static T Get<T>(this ViewDataDictionary viewData, string key) { if (viewData[key] is T) { var viewDataItem = (T)viewData[key]; return viewDataItem; } throw new InvalidCastException(string.Format("View Data does not contain type {0} for key {1}", typeof(T), key)); } public static void Set<T>(this ViewDataDictionary viewData, string key, T value) { viewData[key] = value; } }
public static class ViewDataExtensions
{
    public static T Get<T>(this ViewDataDictionary viewData, string key)
    {
        if (viewData[key] is T)
        {
            var viewDataItem = (T)viewData[key];
            return viewDataItem;
        }
        throw new InvalidCastException(string.Format("View Data does not contain type {0} for key {1}", typeof(T), key));
    }
 
    public static void Set<T>(this ViewDataDictionary viewData, string key, T value)
    {
        viewData[key] = value;
    }
}

I know what you are thinking, this doesn’t stop you from setting TempData the <string, object> way, and you are correct.  To get and set using strong types, you have to have the discipline to use these extension methods.  But, with these tools, you can give yourself a fighting chance.

Leave a Comment

Don’t litter your code with stringly typed settings, mkay?

h9cnk

When using C#, I am kinda a strongly typed bigot and like to use the compiler as much as I can. Since practically every application I have ever worked on has had some sort of setting access from a config file, I felt that there had to be a better way.

So, given this config file:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="StringSetting" value="filepath"/>
<add key="BoolSetting" value="true"/>
<add key="StringListDelimitedSetting" value="one;two;three"/>
</appSettings>
</configuration>
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="StringSetting" value="filepath"/> <add key="BoolSetting" value="true"/> <add key="StringListDelimitedSetting" value="one;two;three"/> </appSettings> </configuration>
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="StringSetting" value="filepath"/>
    <add key="BoolSetting" value="true"/>
    <add key="StringListDelimitedSetting" value="one;two;three"/>
  </appSettings>
</configuration>

I don’t want to litter my code with this everywhere:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
//BAD
string value = System.Configuration.ConfigurationManager.AppSettings["StringSetting"];
if (value == "SOMETHING")
{
//do something
}
//WORSE?
string boolValue = System.Configuration.ConfigurationManager.AppSettings["BoolSetting"];
if (boolValue == "YES")
{
//do something
}
//PRODUCES STRONG TYPE BUT EVEN MORE CODE
string someOtherBoolValue = System.Configuration.ConfigurationManager.AppSettings["SomeOtherBoolSetting"];
bool strongBoolValue;
if (Boolean.TryParse(someOtherBoolValue, out strongBoolValue))
{
if (strongBoolValue)
{
//do something
}
}
//BAD string value = System.Configuration.ConfigurationManager.AppSettings["StringSetting"]; if (value == "SOMETHING") { //do something } //WORSE? string boolValue = System.Configuration.ConfigurationManager.AppSettings["BoolSetting"]; if (boolValue == "YES") { //do something } //PRODUCES STRONG TYPE BUT EVEN MORE CODE string someOtherBoolValue = System.Configuration.ConfigurationManager.AppSettings["SomeOtherBoolSetting"]; bool strongBoolValue; if (Boolean.TryParse(someOtherBoolValue, out strongBoolValue)) { if (strongBoolValue) { //do something } }
//BAD
string value = System.Configuration.ConfigurationManager.AppSettings["StringSetting"];
if (value == "SOMETHING")
{
    //do something
}
  
//WORSE?
string boolValue = System.Configuration.ConfigurationManager.AppSettings["BoolSetting"];
if (boolValue == "YES")
{
    //do something
}
  
//PRODUCES STRONG TYPE BUT EVEN MORE CODE
string someOtherBoolValue = System.Configuration.ConfigurationManager.AppSettings["SomeOtherBoolSetting"];
bool strongBoolValue;
if (Boolean.TryParse(someOtherBoolValue, out strongBoolValue))
{
    if (strongBoolValue)
    {
        //do something
    }
}

So, this is what I do to keep my “stringly” typed settings in one place, strongly typed and make them easily accessible in my code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public static class AppSettingsExtensions
{
public static string StringSetting(this NameValueCollection settings)
{
string setting = settings["StringSetting"];
if (setting != null && !string.IsNullOrWhiteSpace(setting))
{
return setting;
}
return string.Empty;
}
public static bool BoolSetting(this NameValueCollection settings)
{
string setting = settings["BoolSetting"];
if (setting != null && !string.IsNullOrWhiteSpace(setting))
{
bool test;
if (Boolean.TryParse(setting, out test))
{
return test;
}
}
return false;
}
public static IEnumerable<string> StringListDelimitedSetting(this NameValueCollection settings)
{
string setting = settings["StringListDelimitedSetting"];
if (setting != null && !string.IsNullOrWhiteSpace(setting))
{
return setting.Split(Convert.ToChar(";"), Convert.ToChar(",")).ToList();
}
return Enumerable.Empty<string>();
}
}
public static class AppSettingsExtensions { public static string StringSetting(this NameValueCollection settings) { string setting = settings["StringSetting"]; if (setting != null && !string.IsNullOrWhiteSpace(setting)) { return setting; } return string.Empty; } public static bool BoolSetting(this NameValueCollection settings) { string setting = settings["BoolSetting"]; if (setting != null && !string.IsNullOrWhiteSpace(setting)) { bool test; if (Boolean.TryParse(setting, out test)) { return test; } } return false; } public static IEnumerable<string> StringListDelimitedSetting(this NameValueCollection settings) { string setting = settings["StringListDelimitedSetting"]; if (setting != null && !string.IsNullOrWhiteSpace(setting)) { return setting.Split(Convert.ToChar(";"), Convert.ToChar(",")).ToList(); } return Enumerable.Empty<string>(); } }
public static class AppSettingsExtensions
{
    public static string StringSetting(this NameValueCollection settings)
    {
        string setting = settings["StringSetting"];
        if (setting != null && !string.IsNullOrWhiteSpace(setting))
        {
            return setting;
        }
  
        return string.Empty;
    }
  
    public static bool BoolSetting(this NameValueCollection settings)
    {
        string setting = settings["BoolSetting"];
        if (setting != null && !string.IsNullOrWhiteSpace(setting))
        {
            bool test;
            if (Boolean.TryParse(setting, out test))
            {
                return test;
            }
        }
  
        return false;
    }
  
    public static IEnumerable<string> StringListDelimitedSetting(this NameValueCollection settings)
    {
        string setting = settings["StringListDelimitedSetting"];
        if (setting != null && !string.IsNullOrWhiteSpace(setting))
        {
            return setting.Split(Convert.ToChar(";"), Convert.ToChar(",")).ToList();
        }
  
        return Enumerable.Empty<string>();
    }
}

Accessing settings in code now is simple and gives you a strong type:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
//GOOD
string stringSetting = ConfigurationManager.AppSettings.StringSetting();
if (stringSetting == "SOMETHING")
{
//do something
}
//OR
bool boolSetting = ConfigurationManager.AppSettings.BoolSetting();
if (boolSetting)
{
//do something
}
//OR
IEnumerable<string> listSettings = ConfigurationManager.AppSettings.StringListDelimitedSetting();
foreach (string setting in listSettings)
{
//do something
}
//GOOD string stringSetting = ConfigurationManager.AppSettings.StringSetting(); if (stringSetting == "SOMETHING") { //do something } //OR bool boolSetting = ConfigurationManager.AppSettings.BoolSetting(); if (boolSetting) { //do something } //OR IEnumerable<string> listSettings = ConfigurationManager.AppSettings.StringListDelimitedSetting(); foreach (string setting in listSettings) { //do something }
//GOOD
string stringSetting = ConfigurationManager.AppSettings.StringSetting();
if (stringSetting == "SOMETHING")
{
    //do something
}
 
//OR
bool boolSetting = ConfigurationManager.AppSettings.BoolSetting();
if (boolSetting)
{
    //do something
}
 
//OR
IEnumerable<string> listSettings = ConfigurationManager.AppSettings.StringListDelimitedSetting();
foreach (string setting in listSettings)
{
    //do something
}

And yes, this works for connections strings as well, just change the type of the extension:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public static class ConnectionStringExtensions
{
public static string SomeConnectionString(this ConnectionStringSettingsCollection settings)
{
ConnectionStringSettings setting = settings["SomeConnectionString"];
if (setting != null)
{
string connectionString = setting.ConnectionString;
if (!string.IsNullOrWhiteSpace(connectionString))
{
return connectionString;
}
}
return string.Empty;
}
}
public static class ConnectionStringExtensions { public static string SomeConnectionString(this ConnectionStringSettingsCollection settings) { ConnectionStringSettings setting = settings["SomeConnectionString"]; if (setting != null) { string connectionString = setting.ConnectionString; if (!string.IsNullOrWhiteSpace(connectionString)) { return connectionString; } } return string.Empty; } }
public static class ConnectionStringExtensions
{
    public static string SomeConnectionString(this ConnectionStringSettingsCollection settings)
    {
        ConnectionStringSettings setting = settings["SomeConnectionString"];
        if (setting != null)
        {
            string connectionString = setting.ConnectionString;
            if (!string.IsNullOrWhiteSpace(connectionString))
            {
                return connectionString;
            }
        }
 
        return string.Empty;
    }
}

Accessed like:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
string connectionString = ConfigurationManager.ConnectionStrings.SomeConnectionString();
string connectionString = ConfigurationManager.ConnectionStrings.SomeConnectionString();
string connectionString = ConfigurationManager.ConnectionStrings.SomeConnectionString();

And there you have it, that is a tool I like to keep in my toolbox when working with configuration files.

Leave a Comment