Using options pattern in MVC5 legacy applications with web.config
Image from Pexels

Using options pattern in MVC5 legacy applications with web.config

Simple implementation of options pattern in .NET Framework 4.x legacy applications

The story behind

New descendant of .NET Framework, .NET Core, comes with one really useful pattern for reading and using configuration files. It comes with out of the box support for JSON configuration files which are much more readable and give much greater freedom and flexibility on how to organize your settings.

One of the most useful thing in my opinion is native support for options pattern. This pattern allows you to use your configuration through simple, but strongly types POCO classes. Being able to nest things in JSON configurations makes this super easy to do.

However, .NET Core, although it has been around for a while and it is much ore popular than it's ancestor .NET Framework 4.x, it is not higly adopted as much as .NET Framework is. One of the reasons for that is that .NET Framework came a long way since it's first version in 2002 and there are a lot of old projects that are still in active support and development running in Windows environment.

This project are often robust and complex (quite often monolith applications) and it is not that easy, fast or cheap to just simply move them to .NET Core. Although the old framework has some limitations, pattern and practices from .NET Core can be quite easily implemented and used in it.

Limitations and how to overcome them

As I mentioned .NET Core supports JSON loosely structured file configurations out of the box, while .NET Framework relies on really strict form of XML files. So how do we organize POCO structure in these XML (web.config and app.config) files you might wonder?

Well it's a lot simpler that you may think. When you do not have ability to easily create hierarchy, just improvise.

We'll just use "." to split the hierarchy and grouping, something like namespaces in C#. Here is an example in web.config how AppSettings can be used to store configurations with hierarchy:

<configuration>
  <appSettings>
    <add key="services.weather.url" value="https://weather.com"/>
    <add key="services.weather.city" value="Dubai"/>
    <add key="services.weather.refreshinterval" value="5"/>
  </appSettings>


  
</configuration>
    

It is not really organized like in JSON, but you can still easily spot the hierarchy here and you can see that this represents a single logic configuration group. This also means that we can easily translate this to a POCO class instance.

According to this XML configurations, our configuration POCO class would look like something like this:

    public class WeatherServiceConfig
    {
        public String Url { get; }
        public String City { get; }
        public int RefreshInterval { get; }
    }
    

One of the things you maybe noticed is that all properties in this class are readonly. The reason for that is that your configuration loads on startup and should be immutable. You should not be able to change your configuration on the runtime and that is why we have all the configuration POCO properties as readonly with obvious absence of the setter method.

You may wonder how do we than set the values of them, but we'll get to that part as well.

Interface and interface implementation

Now we are getting to the interesting part. First things first, we need an interface for options that we are going to use to implement our options pattern in AS.NET MVC. Since we are only considered only on one thing here and that is value, this is pretty much all we need in this interface.

    public interface IOptions<T>
    {
        T Value { get; }
    }
    

We have interface, so next step is to write an implementation of it. You remember we have only readonly properties of the configuration POCO files? It is time to set them now with our IOptions interface implementation. Although it is not pure OOP approach since we are forcing set of something that is not supposed to be set fro outside, it is doable to set the readonly properties from the outside class and since we are using the new sugar coating syntax without actual fields for properties, we need to access their backing fields. THere is ahort article I wrote on this a while ago Access auto property backing field with reflection and this is what are we going to do here. 

    public class Options<T> : IOptions<T>
    {
        readonly String _keyPrefix;
        readonly T _model;
        public Options(String keyPrefix)
        {
            _keyPrefix = keyPrefix;
            _model = Activator.CreateInstance<T>();

            var propertyNames = ConfigurationManager.AppSettings.AllKeys.Where(k => k.StartsWith(_keyPrefix))
                .ToDictionary(k => k, v => v.Split('.').Last());

            var properties = _model.GetType().GetProperties();

            foreach (var propertyName in propertyNames)
            {
                var property = properties.FirstOrDefault(p => p.Name.Equals(propertyName.Value, StringComparison.InvariantCultureIgnoreCase));
                if (property != null)
                {
                    var value = Convert.ChangeType(ConfigurationManager.AppSettings.Get(propertyName.Key), property.PropertyType);
                    var backingFieldInfo = _model.GetType().GetField($"<{property.Name}>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
                    if (backingFieldInfo != null)
                    {
                        backingFieldInfo.SetValue(_model, value);
                    }
                }
            }
        }

        public T Value
        {
            get
            {
                return _model;
            }
        }
    }

    

Wireing up IOptions<T> and dependency injection with Autofac

Now options wouldn't be much useful without dependency injection. Well ensure that configuration values POCO with values is available throughout the application as a singleton and as an immutable instance.

.NET Core has dependency injection out of the box as well, but in .NET Framework we can easily use some of the popular dependency injection libraries like Autofac or Ninject. I used Autofac for his implementation.

.NET Core has some nice concepts like using extension methods to abstract dependency injection registrations, so there is no valid reason not to use the same approach in .NET Framework projects.For this reason, we can arrange all the heavy lifting of dependency injection inside the extension method and then just use it when we are configuring the IOC container.

    public static class Extensions
    {
        public static ContainerBuilder AddOptions<T>(this ContainerBuilder builder, String keyPrefix)
        {
            builder.RegisterInstance(new Options<T>(keyPrefix)).As<IOptions<T>>().SingleInstance();
            return builder;
        }
    }
    

We can now just simply proceed with the rest of Autofac setup in Global.asax.cs class and wire it all up for the controllers.

    public class WebApiApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);

            ContainerBuilder builder;
            builder = new ContainerBuilder();

            // Configure options
            builder.AddOptions<WeatherServiceConfig>("services.weather");

            // Wire up with MVC WebAPI
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
            var webApiResolver = new AutofacWebApiDependencyResolver(builder.Build());
            GlobalConfiguration.Configuration.DependencyResolver = webApiResolver;
        }
    }
    

Test run

Since we have our dependency injection in place we can reference the IOption interface implementation in our controller constructor and we'll have our POCO class instance available in the whole controller instance.

    public class WeatherController : ApiController
    {
        readonly WeatherServiceConfig _weatherServiceConfig;
        public WeatherController(IOptions<WeatherServiceConfig> options)
        {
            _weatherServiceConfig = options.Value;
        }

        public IHttpActionResult Get()
        {
            return Ok(new
            {
                Location = _weatherServiceConfig.City,
                TemperatureMax = 45,
                TemperatureMin = 39,
                Rain = false
            });
        }

    }
    

If now we run this whole code and put the breakpoint in the controller constructor, we'll see that values from the web.config are available as readonly properties of the POCO class instance that we used where we register IOptions in the IOC container.

Options Debug

Since current setup creates a single immutable instance per POCO class type, we will not have any performance issues as instances will be shared throughout the whole active AppDomain. As they are readonly by nature, it makes than completely thread safe as they are not changed over life time of the application instance. 

Complete sample solution for this article is available for download from the top right section on this page.

References

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.


About the author

DEJAN STOJANOVIC

Dejan is a passionate Software Architect/Developer. He is highly experienced in .NET programming platform includion ASP.NET MVC and WebApi. He likes working on new technologies and exciting challenging projects

CONNECT WITH DEJAN  Loginlinkedin Logintwitter Logingoogleplus Logingoogleplus

JavaScript

read more

SQL/T-SQL

read more

Umbraco CMS

read more

PowerShell

read more

Comments for this article