How to Implement Proper Separation of Concerns in Your Web Forms Application
What is Separation of Concerns?
Development today is not what it used to be. Applications are often far too large for any one person to maintain. Most large applications now require fairly large development teams just to keep them running. As these shops and the applications they develop get bigger, application structure and organization grow in importance. I have become a hard core advocate of proper application organization and Separation of Concerns (SoC). I am sure I sometimes get under my co-workers' skins with my stalwart insistence that our code remain properly commented and organized. In the end though I believe it is worth it, and everyone will be better off.
So what is Separation of Concerns? SoC is a standard practice of dividing up major components of an application and separating them from the rest of the application. There are many different ways to accomplish SoC, but my favorite way is to utilize the magic of Dependency Injection (DI). SoC via DI is the practice of splitting up the application into components, and then resolving any dependencies those components have on other components through the use of injection. What? You say you want that in English? We'll get there shortly, for now just understand that DI is a method we use to accomplish SoC in a very slick fashion; DI is a very abstract concept for a newcomer and I don't expect you to get it right away.
There are a million tutorials on the web already for how to use dependency injection with ASP.NET MVC. That's because MVC is designed with concepts like SoC in mind and it's fun to program in because of it. However, I am going to discuss how to do dependency injection with ASP.NET Web Forms in this tutorial. My reasoning for this is that web forms is still a popular platform that isn't going away anytime soon and there is very little information on how one might accomplish Dependency Injection with it. I had to undertake this task at my job and it turned out to be a week long adventure to figure out how to wedge Separation of Concerns into a platform that does not inherently support it.
How to Separate your Application into Components
On a high level the concept of Separation of Concerns might make perfect sense to you, but often the clarity is lost when you begin to code. Understanding SoC and knowing how to practice it are two completely different things. In order to separate your application properly you must first understand what constitutes a component. A component is not some new programming technology, it is only a concept.
com·po·nent
noun /kəmˈpōnənt/
components, plural
A part or element of a larger whole, esp. a part of a machine or vehicle - stereo components
A component in code would be a section of code that performs one task. An example would be a message sending component. Let's write a sample piece of code that sends a message via an email.
public class CustomMessage
{
public string Title { get; set; }
public string Body { get; set; }
public DateTime Sent { get; set; }
public string To { get; set; }
public string From { get; set; }
}
public partial class SendMessageToUser : Page
{
protected void btnSendMessage_Click(object sender, EventArgs e)
{
CustomMessage newMessage = new CustomMessage
{
Title = "Hey Joe.",
Body = "I'm learning about Separation of Concerns!",
Sent = DateTime.Now,
From = tbxFrom.Text,
To = Request.QueryString["UserID"]
};
bool sentSuccessfully = SendMessage(newMessage);
if (sentSuccessfully)
Response.Write("Message sent!");
else
Response.Write("Message sending failed. Please contact your administrator.");
}
public bool SendMessage(CustomMessage newMessage)
{
try
{
MailMessage newEmail = new MailMessage();
string recipientEmail = newMessage.To;
newEmail.To.Add(recipientEmail);
newEmail.From = newMessage.From;
newEmail.Subject = newMessage.Title;
newEmail.Body = newMessage.Body;
newEmail.IsBodyHtml = false;
SmtpClient mailMan = new SmtpClient("smtp.mailserver.com");
mailMan.Send(newEmail);
return true;
}
catch
{
return false;
}
}
}
As you can see we just put together a CustomMessage
object and passed it into a method designed to send that message as an email to the recipient. Everything here happened in the codebehind of a web forms page. This, in my opinion, is a very BAD way to program. This does not adhere to separation of concerns at all. Unfortunately this is the way 80% of developers out there would write this. It is easy to think it is no big deal and that the code would not be hard to maintain. What if someday in the future you decided that you needed to store these messages in a database for some custom message feature on your site instead of sending them as emails? You would have to go all over your site and find these little methods where you sent an email and modify them to instead store information in a database. What a pain!
The proper way to write this code would be to separate it out into its proper components. In this example, the SendMessage()
method would be the perfect candidate for a piece of code that should be factored out into a separate component. Emails could be sent from all over your site so you should only write that code once and then call it instead.
public class CustomMessage
{
public string Title { get; set; }
public string Body { get; set; }
public DateTime Sent { get; set; }
public string To { get; set; }
public string From { get; set; }
}
public partial class SendMessageToUser : Page
{
protected void btnSendMessage_Click(object sender, EventArgs e)
{
CustomMessage newMessage = new CustomMessage
{
Title = "Hey Joe.",
Body = "I'm learning about Separation of Concerns!",
Sent = DateTime.Now,
From = tbxFrom.Text,
To = Request.QueryString["UserID"]
};
MessageSender mailMan = new MessageSender();
bool sentSuccessfully = mailMan.SendMessage(newMessage);
if (sentSuccessfully)
Response.Write("Message sent!");
else
Response.Write("Message sending failed. Please contact your administrator.");
}
}
public class MessageSender
{
public bool SendMessage(CustomMessage newMessage)
{
try
{
MailMessage newEmail = new MailMessage();
string recipientEmail = newMessage.To;
newEmail.To.Add(recipientEmail);
newEmail.From = newMessage.From;
newEmail.Subject = newMessage.Title;
newEmail.Body = newMessage.Body;
newEmail.IsBodyHtml = false;
SmtpClient mailBox = new SmtpClient("smtp.mailserver.com");
mailBox.Send(newEmail);
return true;
}
catch
{
return false;
}
}
}
Easy right? Now you have a class that you can call from all over your application and pass in a CustomMessage
object. But that's not good enough. It would still be a pain to swap out this code for something that sends messages completely different (like storing them in the database). If I didn't want to lose the email sending code I would have to copy it and save it somewhere, then I would have to write all new code in the class to persist the message to the database. Ugh, so painful!
This is where interfaces come in. Interfaces are abstract classes that only contain method signatures. Regular classes have the ability to inherit from interfaces. This gives the class a sort of mandatory template to follow. A class implementing an interface MUST implement all methods defined in the interface. The beauty of putting code behind an interface is its interoperability. If one component depends on another component, an interface can be used to bridge that gap. This makes it so that if you ever want to change the component behind the interface you just write a new component that inherits from the same interface and then use dependency injection to map the new component to instances of the interface.
If these concepts are completely new to you then you are probably scratching your head right now. Let me show you an example using the same code from above. In our code we essentially have two components. One of them is the page we are working with and the other is the message sending component. It should be obvious that the page has a dependency on the message sender. Let's re-factor the code to better reflect this dependency.
public class CustomMessage
{
public string Title { get; set; }
public string Body { get; set; }
public DateTime Sent { get; set; }
public string To { get; set; }
public string From { get; set; }
}
public Interface IMessageSender
{
bool SendMessage(CustomMessage newMessage);
}
public class MessageSender : IMessageSender
{
public bool SendMessage(CustomMessage newMessage)
{
try
{
MailMessage newEmail = new MailMessage();
string recipientEmail = newMessage.To;
newEmail.To.Add(recipientEmail);
newEmail.From = newMessage.From;
newEmail.Subject = newMessage.Title;
newEmail.Body = newMessage.Body;
newEmail.IsBodyHtml = false;
SmtpClient mailBox = new SmtpClient("smtp.mailserver.com");
mailBox.Send(newEmail);
return true;
}
catch
{
return false;
}
}
}
public partial class SendMessageToUser : Page
{
public IMessageSender MessageSendingComponent { get; set; }
protected void btnSendMessage_Click(object sender, EventArgs e)
{
CustomMessage newMessage = new CustomMessage
{
Title = "Hey Joe.",
Body = "I'm learning about Separation of Concerns!",
Sent = DateTime.Now,
From = tbxFrom.Text,
To = Request.QueryString["UserID"]
};
bool sentSuccessfully = MessageSendingComponent.SendMessage(newMessage);
if (sentSuccessfully)
Response.Write("Message sent!");
else
Response.Write("Message sending failed. Please contact your administrator.");
}
}
Interfaces are not actual classes. You cannot instantiate an interface like you can a regular object. The only way you can use an interface like a real object is to pass it a real object that inherits from it. For example, take the line Car myCar = new Car();
. You obviously have a class called Car
and you are instantiating an object of type Car
. Now pretend your car object has a method called HonkHorn()
. You would write that method to perform a specific task and honk the horn of your imaginary car. Let's pretend you have another object called Driver
(Driver carDriver = new Driver();
). Now say Driver
has a dependency on a Car
, you can't have a Driver
without a Car
. The Driver
wants to call the Car
's HonkHorn method so he does and the car's horn honks.
Pretend now that you want your driver to drive a train instead of a car and you want the HonkHorn method to do something completely different because the horn is very different. You would have to go find the Car
property on your Driver
object and change it to be of type Train
. Well all I really want my Driver
to know is that he is driving a vehicle and should honk the horn, I don't want him to care about what kind of vehicle it is or know what the horn does when he honks it. To do this I create an abstract interface that hides the underlying object from him. All he has is the interface which tells him what methods are available to him. He has no idea what any of the methods will exactly do when he calls them. The most he could know is if the method returns some data. For instance, IVehicle
could have a method like int ReadFuelGauge();
. The driver would know he has a method called ReadFuelGuage()
that returns an integer specifying the amount of fuel left; he would not know if that method checked the fuel tank of a car or if it checked the coal in a steam engine.
Pretend you have an interface called IVehicle
and it defines a method called HonkHorn()
. You then have two real objects called Car
and Train
, they both inherit from IVehicle
. Because they inherit from this interface and the interface defines a method called HonkHorn()
they must both define a method called HonkHorn()
. Both methods can do two completely different tasks, but they must both have this method as required by the interface. Now my Driver
has a constructor that expects some type of vehicle like this public Driver(IVehicle driversVehicle)
. When I instantiate my Driver
object I could pass in either a Car
or a Train
and the driver would have no idea which one it was. He could happily driversVehicle.HonkHorn()
as much as he wanted and the appropriate method would be fired. So only the code outside of the driver object knows what vehicle the driver has because it injected the driver's dependency on some vehicle.
Car myCar = new Car();
Driver someDriver = new Driver(myCar);
or
Train myTrain = new Train();
Driver someDriver = new Driver(myTrain);
Catching on? Now let's take another look at our message sending code. MessageSender
inherits from the interface IMessageSender
(It is a standard naming convention to start interface names with an 'I'). The interface defines one method called SendMessage()
that must be defined in any classes inheriting from it. The method signature in the implementing class must match the method signature in the interface EXACTLY. This makes it possible to write multiple components using the same interface! Notice that the page defines its dependency on IMessageSender by making it a public property on the page. How then does this property get set? And even when we figure out where the property gets set, how do we know what component to use if we have multiple components that inherit from IMessageSender? Ninject takes the stage.
Ninject is what is known as an Inversion of Control (IoC) container or Dependency Injection (DI) container. In simple terms IoC is just a way to resolve dependencies in a single common location, inverting control over how components are tied together. Ninject is a code library built for almost all versions of .NET. It is able to resolve dependencies based on mappings that you define; it will look at your components and find any dependencies to see if the interface it is trying to populate is in it's list of defined bindings. It also has an excellent library of extensions that extend Ninject for whatever .NET platform you happen to be working with.
One of the Ninject extensions is called Ninject.Web and allows easy integration between Ninject and Web Forms. Ninject needs to have three things happen in order to work properly.
- A Kernel needs to be created. (A Kernel is the core Ninject object. Odd name for it, but that's what it is.)
- Bindings need to be defined.
- The Kernel's inject method needs to be called and passed the instance of an object with a dependency that needs to be resolved.
The kernel creation and the bindings both happen in the Global.asax file (Global.asax is a file in .NET web applications that contains event handlers that are raised during various events that occur in your application, such as Application_Start
, Application_End
, and even Application_Error
), but first we need to add references to our libraries. For web forms you will need three libraries: Ninject.dll, Ninject.Web.dll, Ninject.Web.Common.dll. For some reason they recently split up Ninject.Web into two assemblies and we now have to reference Ninject.Web.Common in addition; it's kind of annoying but oh well. After you've referenced the assemblies you now need to modify your Global.asax file in your web forms application.
- Add a using statement at the top: "using Ninject;" and "using Ninject.Web.Common;".
- Modify the class to inherit from "NinjectHttpApplication" instead of "HttpApplication".
- Then you need to override the abstract method "CreateKernel" that is defined in NinjectHttpApplication.
It should look something like this:
using Ninject;
using Ninject.Web.Common;
namespace Company.ProjectName
{
public class Global : NinjectHttpApplication
{
protected override IKernel CreateKernel()
{
IKernel kernel = new StandardKernel();
return kernel;
}
}
}
In the code above you've made your application inherit from NinjectHttpApplication and you've overridden the CreateKernel method to create your custom kernel object. An important note when inheriting from NinjectHttpApplication is that it handles the Application_Start
and Application_End
events, which means that yours will no longer be called. This is inconvenient if you had some code in those event handlers (like RegisterRoutes if you are using route mapping). To get around this NinjectHttpApplication provides two methods that can be overridden, OnApplicationStarted()
and OnApplicationStopped()
. Now we need to add your bindings. Bindings are how you define the mappings and tell Ninject which components to use when it is resolving a dependency on a certain interface. Modify the above code snippet to look like this:
using Ninject;
using Ninject.Web.Common;
namespace Company.ProjectName
{
public class Global : NinjectHttpApplication
{
protected override IKernel CreateKernel()
{
RegisterRoutes(RouteTable.Routes);
IKernel kernel = new StandardKernel();
kernel.Bind<IMessageSender>().To<MessageSender>().InRequestScope();
return kernel;
}
}
}
In plain English what you just did was tell Ninject "Whenever you are told to resolve a dependency for IMessageSender, create an instance of MessageSender and pass that in." Don't worry too much about the .InRequestScope()
part. That simply tells Ninject to hang on to its instance of MessageSender only for the current page request; the next request will create a new instance of MessageSender again. Awesome huh? But now how do we tell Ninject to resolve that dependency on IMessageSender on our page? We will find out shortly, but first I need to explain the different types of injection.
Types of Injection
There are three types of dependency injection with Ninject but I'm only going to talk about two of them. Method injection is rarely used and is beyond the scope of this tutorial.
The first and most common type of dependency injection is constructor injection. You create the dependency by making the constructor of your object accept the component it is dependent upon as a parameter. Ninject then takes care of the dependency at the object's creation. By default Ninject will pick the constructor that accepts the most parameters that it is capable of resolving. Usually you only have one constructor though and Ninject should know how to map it. In the event you have multiple constructors and you want to tell Ninject a specific one to use, all you have to do is add an [Inject]
attribute above the constructor. This will force Ninject to use that constructor when creating the object and resolving dependencies.
The second type of dependency injection is called property injection and it's the type we are going to use with our web form. The reason for this is that our web form is of type Page
and it has already been instantiated, which means we cannot inject anything into its constructor. Luckily, we can still inject dependencies into public properties by decorating the properties with the [Inject]
attribute.
Lets modify our code now. There are two things that need to happen. We need to add the [Inject]
attribute over that public property we added earlier and we need to change our page to inherit from Ninject.Web.PageBase
instead of Page
. Ninject.Web.PageBase
calls up Ninject and passes in the page for injection. Ninject will then look at the page and locate all resolvable dependencies and inject them. Make your codebehind look like this:
using Ninject.Web;
public partial class SendMessageToUser : PageBase
{
[Inject]
public IMessageSender MessageSendingComponent { get; set; }
protected void btnSendMessage_Click(object sender, EventArgs e)
{
CustomMessage newMessage = new CustomMessage
{
Title = "Hey Joe.",
Body = "I'm learning about Separation of Concerns!",
Sent = DateTime.Now,
From = tbxFrom.Text,
To = Request.QueryString["UserID"]
};
bool sentSuccessfully = MessageSendingComponent.SendMessage(newMessage);
if (sentSuccessfully)
Response.Write("Message sent!");
else
Response.Write("Message sending failed. Please contact your administrator.");
}
}
That's it! You now have a page with a dependency on an abstract (interfaced) component. You told Ninject how to map the real component to the abstract one and you let it do the work of resolving it for you on your page. Now just think how easy it would be to change your message sending component. Here is a quick new component that writes messages to an imaginary database instead of sending emails.
public class MessageWriter : IMessageSender
{
private SqlConnection _connection = new SqlConnection("your connection string here.");
public bool SendMessage(CustomMessage newMessage)
{
try
{
SqlCommand command = new SqlCommand("INSERT INTO tblMessages VALUES (@Title, @Body, @Sent, @To, @From"), _connection)
command.Parameters.Add(new SqlParameter("Title", newMessage.Title));
command.Parameters.Add(new SqlParameter("Body", newMessage.Body));
command.Parameters.Add(new SqlParameter("Sent", newMessage.Sent));
command.Parameters.Add(new SqlParameter("To", newMessage.To));
command.Parameters.Add(new SqlParameter("From", newMessage.From));
_connection.Open();
command.ExecuteNonQuery();
_connection.Close();
return true;
}
catch
{
return false;
}
}
}
Wasn't that so simple? You just wrote a new component. Now just watch how easy it is to swap out the old component for the new one.
Open up Global.asax and change this:
kernel.Bind<IMessageSender>().To<MessageSender>().InRequestScope();
To this:
kernel.Bind<IMessageSender>().To<MessageWriter>().InRequestScope();
You are done! With one line of code you just made every single component in your application with a dependency on IMessageSender switch over to using the new database writer component and all those components are none the wiser.
I could end the tutorial here but I wanted to throw in one more note. Pretend that your message sending component had a dependency on another component. You are now free (and encouraged) to use constructor injection. You don't even need to use the [Inject]
attribute unless you have more than one constructor and Ninject is getting confused. Since Ninject is already passing your component in as a dependency on something else, it will also resolve all the dependencies your component has while it's at it! Slick huh? Let's see an example using our MessageWriter
component we just wrote. Modify it to contain a dependency on a SqlConnection
by adding a constructor.
public class MessageWriter : IMessageSender
{
public MessageWriter(SqlConnection connection)
{
_connection = connection;
}
private SqlConnection _connection;
public bool SendMessage(CustomMessage newMessage)
{
try
{
SqlCommand command = new SqlCommand("INSERT INTO tblMessages VALUES (@Title, @Body, @Sent, @To, @From"), _connection)
command.Parameters.Add(new SqlParameter("Title", newMessage.Title));
command.Parameters.Add(new SqlParameter("Body", newMessage.Body));
command.Parameters.Add(new SqlParameter("Sent", newMessage.Sent));
command.Parameters.Add(new SqlParameter("To", newMessage.To));
command.Parameters.Add(new SqlParameter("From", newMessage.From));
_connection.Open();
command.ExecuteNonQuery();
_connection.Close();
return true;
}
catch
{
return false;
}
}
}
Now that you have refactored your message writing component to depend on a connection object we have to pass that in when we instantiate our MessageWriter
. So how do we pass in this dependency when Ninject is the one that is building our object for us and passing it into a property on our page as a dependency? In Global.asax add a new binding that looks like this:
kernel.Bind<SqlConnection>.ToSelf().WithConstructorArgument("connectionString", "your connection string here").InRequestScope();
Now when Ninject instantiates your MessageWriter
and injects it into your page it will automatically see that it has a dependency on a SqlConnection
and it will resolve that too!
Note that by default if you ask Ninject for a concrete object (not an interface) it will automatically try to create an instance of it even if you have no binding in Global.asax. However, we wouldn't want it to create a new one using the default constructor because we want it to include a connection string. This is why we do a .ToSelf()
binding followed by a .WithConstructorArgument()
. This tells Ninject to still instantiate SqlConnection
objects but to also include the connection string when doing so.
You can cascade dependencies like this all the way down. You could have your MessageWriter
depend on another component's interface and the component Ninject passes in for that could then have yet another dependency on an entirely different component. For example:
Classes
public class MessageWriter : IMessageSender
{
public MessageWriter(SqlConnection connection, ISpellChecker spellChecker, ILogWriter logWriter) { ... }
}
public class EnglishSpellChecker : ISpellChecker
{
public EnglishSpellChecker() { ... }
}
public class DbLogWriter : ILogWriter
{
public DbLogWriter (SqlConnection connection) { ... }
}
Global.asax
kernel.Bind<IMessageSender>().To<MessageWriter>().InRequestScope();
kernel.Bind<ISpellChecker>().To<EnglishSpellChecker>().InRequestScope();
kernel.Bind<ILogWriter>().To<DbLogWriter>().InRequestScope();
kernel.Bind<SqlConnection>().ToSelf().WithConstructorArgument("connectionString", "your connection string here").InRequestScope();
That is all it takes to wire all those components together. Just by including a public IMessageSender
property on your page with the [Inject]
attribute all of these components would be created and passed in where they are needed with no extra effort on your part.
- Ninject will see your
IMessageSender
property on your page and try to create a newMessageWriter
to pass in. - Ninject will see
MessageWriter
needs aSqlConnection
,ISpellChecker
, andILogWriter
and it will try to create one of each of those to pass in. - Ninject will see
SqlConnection
has a binding that includes a constructor argument for the connection string so it will include that when creating a newSqlConnection
. - Ninject will see that it has a binding for
ISpellChecker
and it will try to create anEnglishSpellChecker
to pass in. - Ninject will see that it has a binding for
ILogWriter
and it will try to create aDbLogWriter
to pass in. - Ninject will see that
DbLogWriter
also depends on aSqlConnection
and it will pass in the one it created forMessageWriter
.
All that work was done for you. You didn't even have to think about it. You just set up your classes and add Ninject bindings. That's it.
I hope this tutorial helped you to understand separation of concerns and how to accomplish that through the use of dependency injection.