2009

Volume 24 Number 0

Extreme ASP.NET Makeover - Death of a Singleton

By Kyle Baley, Donald Belcham, James Kovacs | 2009

We’re now on the eighth installment of Extreme ASP.NET Makeover. In part 7, we examined the singleton pattern and refactored away the AuthChecker singleton. AuthorizationServices, which previously used the AuthChecker singleton, now depends on the IAuthorizationChecker interface and simply creates an instance of the AuthorizationChecker class in its constructor by calling the new operator. In this installment, we will discuss better ways to manage dependencies in our applications using an inversion of control container. If you are not familiar with inversion of control containers or the related concepts of dependency inversion and dependency injection, take some time to read James’ article on those topics from the March 2008 issue of MSDN Magazine, “Loosen Up: Tame Your Software Dependencies for More Flexible Apps.”

The Tangled Web we Weave

Let’s take a closer look at AuthorizationServices. AuthorizationServices depends on IAuthorizationChecker, which is implemented by AuthorizationChecker. In turn, AuthorizationChecker depends on Settings to retrieve the configured ISettingsStorageProviderV30, which can be a SettingsStorageProvider, SqlSettingsStorageProvider, or other implementation. Now, SettingsStorageProvider depends on IHostV30 and its default implementation by Host. Host is a “God object” in ScrewTurn Wiki. According to Wikipedia, “a God object is an object that knows too much or * does too much*.” A God object is a software anti-pattern.

Host is a coordinator of a wide variety of unrelated activities in ScrewTurn. It handles everything from retrieving settings values to sending emails to handling backups to finding users, Wiki pages, namespaces, categories, and more, as shown in Figure 1.


Figure 1 IHostV30

Many of these responsibilities are delegated to sub-objects, and Host only acts as the coordinator of these services. This is better than having all that functionality actually coded in Host itself. Host does still present a problem in that its delicate coordination of all the other objects in the system, especially during application startup, means that it is difficult to refactor the overall system. Moving startup code from one class to another causes breakages as the ordering of construction is slightly changed and previously working code throws NullReferenceExceptions as dependencies are unexpectedly null due to construction orderings.

Let’s take a look at Host’s constructor:

public Host() {
    customSpecialTags = new Dictionary<string, CustomToolbarItem>(5);
}

In its constructor, Host creates a single Dictionary. There is no indication that Host has a dependency problem. Host’s tangled web of dependencies is hidden due to its overuse of Singletons and static helper classes. For example, let’s take a look at a few Host methods:

public UserInfo[] GetUsers() {
    return Users.Instance.GetUsers().ToArray();
}

public PageContent GetPageContent(PageInfo page) {
    return Content.GetPageContent(page, true);
}

public string Format(string raw) {
    return Formatter.Format(raw, FormattingContext.Unknown, null);
}

Note the use of the Users singleton and the static Content an Formatter classes in the above code. Without actually looking at Host’s implementation code, there is no way to tell that it depends on these other classes. To make matters even more complicated, Users (and other dependent classes) depend on Host! If constructors aren’t run in the correct order and before certain methods are called, NullReferenceExceptions will be your reward. All of the construction logic is carefully orchestrated by StartupTools, which is responsible for constructing and initializing all singletons within the application in the correct order.

Seeking Salvation

God objects lead us down a road unto madness. Host’s dependencies are so tangled and interdependent that the result is only somewhat better than a single, monolithic object. So how do we chart a course back to sanity?

Dependencies in software are not fundamentally the problem here. Without dependencies, we wouldn’t be able to write much more than “Hello, World!”-style applications. (Even a simple application like Hello, World!  requires dependencies on System.Console and System.String, as well as the CLR.) The problem is unchecked and unfettered dependencies between objects that do not need to know about each other. Before we can solve the problem of unnecessary dependencies, we need to make those dependencies obvious. How can we construct our classes/objects so that their dependencies are obvious? Make dependencies apparent by declaring them in the classes constructor. For example, let’s look at AuthorizationServices’ constructor:

public AuthorizationServices(IAuthorizationChecker authorizationChecker) {
    this.authorizationChecker = authorizationChecker;
}

Simply by looking at the constructor’s signature, we can tell that AuthorizationServices requires an IAuthorizationChecker to perform its work. (You probably already knew that because we implemented it in part 7.) The concrete implementation of IAuthorizationChecker is AuthorizationChecker:

public AuthorizationChecker() {
    settingsProvider = Settings.Instance.Provider;
}

Notice that you don’t know that AuthorizationChecker depends on the static Settings class. You have to read AuthorizationChecker’s constructor implementation to figure this out. While not hard, it is not immediately obvious that AuthorizationChecker depends on ISettingsStorageProviderV30, which is the type return by the Provider property. How can we make this more obvious? By declaring our intent in the constructor parameters, like so:

public AuthorizationChecker() : this(Settings.Instance.Provider) {
}
 
public AuthorizationChecker(ISettingsStorageProviderV30 settingsProvider) {
    if(settingsProvider == null)
        throw new ArgumentNullException("settingsProvider");
 
    this.settingsProvider = settingsProvider;
}

Our other code depends on the parameterless constructor. We will leave it in place for now and chain to an overloaded constructor that takes a ISettingsStorageProviderV30. Our dependency on ISettingsStorageProviderV30 has now been made obvious.

We are not out of the proverbial woods yet. Other dependencies are lurking within AuthorizationChecker, namely the static classes AuthTools and AclEvaluator. We can make them obvious dependencies by changing them from static classes to instances and moving their initialization to the constructor.

 

A Topsy-Turvy World

The constructor signature for AuthorizationChecker now looks like this:

public AuthorizationChecker(ISettingsStorageProviderV30 settingsProvider, 
IAuthTools authTools, IAclEvaluator aclEvaluator) { ... }

We can clearly see that AuthorizationChecker depends on implementations of ISettingsStorageProviderV30, IAuthTools, and IAclEvalator, but we are not concerned with the actual implementations. This is the dependency inversion principle at work.

"High-level modules should not depend on low-level modules. Both should depend on abstractions." – Robert C. Martin (https://objectmentor.com/resources/articles/dip.pdf)

In this case, AuthorizationChecker and AuthTools don’t depend on each other, but depend on the abstraction provided by IAuthTools. The same is true for AuthorizationChecker’s other two dependencies, ISettingsStorageProviderV30 and IAclEvaluator.

We have decoupled AuthorizationChecker from its dependencies. The question remains that AuthorizationChecker still needs these dependencies supplied at run-time to actually be able to execute. The simplest way to supply these dependences as default implementations via AuthorizationChecker's parameterless constructor:

public AuthorizationChecker() : this(Settings.Instance.Provider, 
new AuthTools(), new AclEvaluator()) {
}

Of Containers and Castles

A more flexible way to supply dependencies is to use an Inversion of Control container, such as Castle Windsor. Castle Windsor is a set of assemblies that we reference from our code. We simply register all of our dependencies in the container and then ask the container for an implementation. The container walks the dependency chain, creates objects in the correct order, supplying dependencies as it creates objects further up the dependency chain until it can finally return us a fully constructed object. Consider the following code:

var authorizationServices = IoC.Resolve<IAuthorizationServices>();

The IoC is a static gateway class that provides an abstraction so that your code is not dependent on a particular IoC container for retrieving dependencies. It is similar in interface and approach to Microsoft’s Common Service Locator available on CodePlex. You can read more about the IoC static gateway in “Loosen Up: Tame Your Software Dependencies for More Flexible Apps”.

In order for Castle Windsor to satisfy this request for an implementation of IAuthorizationServices, we’ll need to register the dependencies:

var ssp = ProviderLoader.LoadSettingsStorageProvider(WebConfigurationManager.AppSettings
["SettingsStorageProvider"]);
container.Register(
    AllTypes.FromAssembly(Assembly.GetExecutingAssembly())
            .Where(x => x.Namespace.StartsWith("ScrewTurn.Wiki.Services"))
            .WithService.FirstInterface(),
    Component.For<IAclEvaluator>().ImplementedBy<AclEvaluator>(),
    Component.For<ISettingsStorageProviderV30>().Instance(ssp)
);

This code is located in the ScrewTurn.Wiki.Core assembly. Rather than manually configuring each dependency individually, we use a convention-based approach whereby any types in the ScrewTurn.Wiki.Services namespace are registered via their first interface. This means that AuthorizationServices is registered as IAuthorizationServices, AuthTools is registered as IAuthTools, and similarly for other types in this namespace. This has the advantage that by placing services in the ScrewTurn.Wiki.Services namespace, they will automatically be registered in the container without requiring explicit configuration. Other classes can then take dependencies on these services simply by adding the appropriate interface to their own constructor.

AclEvaluator is located in the ScrewTurn.Wiki.AclEngine assembly. Since it is the only dependency in this assembly, we register it explicitly. There isn’t a great deal of value from having a separate assembly for ScrewTurn.Wiki.AclEngine and I would consider merging the AclEngine project into Core. AclEvaluator could then be placed in the ScrewTurn.Wiki.Services namespace and auto-registered like the other dependencies. For the moment, AclEvaluator serves as an example of how to explicitly configure dependencies in Castle Windsor.

ScrewTurn Wiki has code that dynamically loads a particular ISettingsStorageProviderV30 implementation specified by a fully qualified class name in a configuration file. Although we could use Castle Windsor’s configuration facilities to configure and load the appropriate assembly and implementation, that is not the current focus. We can leverage the existing loading mechanism by supplying Castle Windsor with a fully constructed instance via the Component.For<T>().Instance(obj) syntax.

Windsor provides many more configuration options, including overrides, parameters, XML configuration, and more. You can find a lot more information on configuration options in Castle Windsor in my article, Bricks and Mortar: Building a Castle.

Now that AuthorizationServices and all of its dependencies have been registered with the container, we can successfully resolve it from the container:

var authorizationServices = IoC.Resolve<IAuthorizationServices>();

To service this request, Windsor will look for an implementer of IAuthorizationServices, which is AuthorizationServices. Windsor will then examine AuthorizationServices constructor, specifically the constructor parameters:

public AuthorizationServices(IAuthorizationChecker authorizationChecker) {
    this.authorizationChecker = authorizationChecker;
}

Windsor will look to see if it has an implementation registered for IAuthorizationChecker, which it does in the form of AuthorizationChecker. Looking at AuthorizationChecker’s constructors:

public AuthorizationChecker() : this(Settings.Instance.Provider, new AuthTools(), 
new AclEvaluator()) {
}
 
public AuthorizationChecker(ISettingsStorageProviderV30 settingsProvider, 
IAuthTools authTools, IAclEvaluator aclEvaluator) {
    ...
}

Windsor has two constructors to choose from. It starts from the constructor with the most overloads. It will see if it has implementations for all of the constructor parameters. (If it can’t find implementations for all constructor parameters, Windsor will attempt the next most overloaded constructor.) In this case, it will see that it needs to find an implementation for ISettingsStorageProviderV30, IAuthTools, and IAclEvaluator. Windsor has an instance of ISettingsStorageProviderV30 that we supplied to it earlier. It can also create instances of AuthTools and AclEvaluator because they have no further dependencies. Windsor passes the newly created AuthTools and AclEvaluator, as well as the pre-built ISettingsStorageProviderV30 to AuthorizationChecker’s constructor. Windsor then passes the newly created AuthorizationChecker to AuthorizationServices’ constructor. Finally, Windsor is able to pass back AuthorizationServices to satisfy the call to IoC.Resolve<IAuthorizationServices>().

Note that Windsor is responsible for managing the lifetime of objects registered in the container. Windsor’s default lifetime strategy is singleton. That means that multiple classes depending on IAuthorizationServices will all receive the same instance of AuthorizationServices. This instancing policy is easily changed on a per-service basis and includes options such as per-thread, per-Web-request, transient, and pooled. You can even create your own instancing policies if none of the built-in ones are appropriate.

With the container responsible for wiring dependencies between objects, we can remove the parameterless constructors as they are no longer required:

If AuthorizationChecker required another dependency, we would simply declare that fact by adding another constructor parameter with a type of the required service interface. We would then create a concrete class in the ScrewTurn.Wiki.Services namespace that implements that service interface.

Managing the Container

I have seen many projects place all container configuration into a single XML or C# file. This is especially unwieldy if explicitly configuring dependencies individually, which is why I prefer the convention-over-configuration approach discussed above. By placing dependencies in certain assemblies and namespaces, they are automatically registered. (Note that the convention-over-configuration approach is not available with XML files, although XML files are useful for specifying deployment-time overrides if necessary. For more information about this technique, see Bricks and Mortar: Building a Castle.) Placing all of our conventions in a single C# file can quickly become cumbersome as we have unrelated conventions bundled together.

Let’s take a look at an approach that allows us to separate our conventions into smaller, simpler classes. It all starts with the ApplicationBootstrapper, which is called during application startup:

public void Configure() {
    container = new WindsorContainer();
    IoC.Initialize(new WindsorDependencyResolver(container));
 
    RegisterFacilityStartupTasks();
    ExecuteFacilityStartupTasks();
    RegisterContainerStartupTasks();
    ExecuteContainerStartupTasks();
    RegisterStartupTasks();
    ExecuteStartupTasks();
}

We start by creating a new WindsorContainer and using it to initialize the IoC static gateway. We then alternately register and execute FacilityStartup, ContainerStartup, and Startup tasks, as shown in Figure 2.

Figure 2 Execute FacilityStartup, ContainerStartup, and Startup Tasks

private void RegisterFacilityStartupTasks() {
    RegisterAllTypesBasedOn<IFacilityStartupTask>();
}

private void RegisterContainerStartupTasks() {
    RegisterAllTypesBasedOn<IContainerStartupTask>();
}

private void RegisterStartupTasks() {
    RegisterAllTypesBasedOn<IStartupTask>();
}

private void RegisterAllTypesBasedOn<T>() {
    assembliesToScan.ForEach(assembly => 
        container.Register(
            AllTypes.FromAssembly(assembly)
                    .BasedOn<T>()
                    .WithService.FirstInterface())
    );
}

The list of assemblies to scan for startup tasks is provided via AssemblyBootstrapper’s constructor and defaults to Assembly.GetExecutingAssembly() if left unspecified. We look through each assembly for types implementing IFacilityStartupTask, IContainerStartupTask, and IStartupTask and register them against the appropriate interface. (Facilities are Windsor’s primary extension mechanism. You can use facilities to implement everything from automatic transaction management to logging to array parameter resolution and more.) Next, we need to execute each of the startup tasks:

private void ExecuteFacilityStartupTasks() {
    var tasks = container.ResolveAll<IFacilityStartupTask>();
    tasks.ForEach(task => task.Execute(container));
}

private void ExecuteContainerStartupTasks() {
    var tasks = container.ResolveAll<IContainerStartupTask>();
    tasks.ForEach(task => task.Execute(container));
}

private void ExecuteStartupTasks() {
    var tasks = container.ResolveAll<IStartupTask>();
    tasks.ForEach(task => task.Execute());
}

Note that IFacilityStartupTask|IContainerStartupTask.Execute both take an IWindsorContainer, as their purpose is to configure the container. IStartupTask.Execute() is to perform other types of initialization once the container is ready to be used and therefore doesn’t accept an IWindsorContainer.

The individual startup tasks are small and self-contained. For example, take a look at the ServiceRegistration class, which is responsible for registering application-related services:

public class ServicesRegistration : IContainerStartupTask {
    public void Execute(IWindsorContainer container) {
        container.Register(
            AllTypes.FromAssembly(Assembly.GetExecutingAssembly())
                .Where(x => x.Namespace.StartsWith("ScrewTurn.Wiki.Services"))
                .WithService.FirstInterface(),
            Component.For<IAclEvaluator>().ImplementedBy<AclEvaluator>()
        );
    }
}

You can see the convention responsible for registering all services in the ScrewTurn.Wiki.Services namespace. There is also the extra configuration for the AclEvaluator. If we choose to combine the AclEngine project into Core, we could eliminate the extra configuration step for AclEvaluator.

We have a completely separate and independent IContainerStartupTask for Host and Provider registration:

public class HostAndProviderRegistration : IContainerStartupTask {
    public void Execute(IWindsorContainer container) {
        container.Register(
            Component.For<IHostV30>().ImplementedBy<Host>());
 
        var ssp = ProviderLoader.LoadSettingsStorageProvider(
            WebConfigurationManager.AppSettings["SettingsStorageProvider"]);
        container.Register(
            Component.For<ISettingsStorageProviderV30>().Instance(ssp));
    }
}

As we break apart the “God object” that is the Host, we can modify its container registration in one place without affecting the registration of the services. If we need to perform additional unrelated container configuration, we create another class that implements the IFacilityStartupTask or IContainerStartupTask without affecting other configuration that is already taking place.

To wrap up, let’s refactor StartupTools.Startup() into a IStartupTask where it belongs:

 

Conclusion

Although it is not initially obvious, the ScrewTurn Wiki codebase suffers from dependency problems. Tight coupling of implementation classes caused by singletons and static classes result in a morass of objects, which is difficult to refactor. Dependency cycles between objects result in sensitive constructor and method orderings, which can result in unexpected NullReferenceExceptions after apparently innocuous changes to the code.

In this article, we have made dependencies obvious by changing them into constructor parameters and using an IoC container to wire together the dependencies. Using convention-over-configuration approaches eases the introduction of new functionality, without requiring explicit configuration. We have introduced a reusable ApplicationBootstrapper, whereby new startup tasks can be added as the application grows without modifying existing code. The resulting codebase should be more flexible and maintainable over time.