Thursday, March 18, 2010

ASP.NET MVC isn't SOLID (or How to Rely on Abstractions in Views and Controllers)

MVC is a great framework for building web applications, however, there's at least one SOLID principle I feel could be implemented more thoroughly: Dependency Inversion Principle. Dependency Inversion Principle states that "code should depend on abstractions, not concretions." This can be seen in the views, where the generic type parameter of strongly-typed views is typically based on a concrete model:

<%@ Page Title="" Language="C#" MasterPageFile="../Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MyNamespace.Widget>" %>

It can also be seen in the controller, where POST actions may have parameters that are concrete:

[HttpPost]
public void Details([Bind] Widget widget)
{
// do something with the widget.
}

Ideally, it'd be nice to rely on an interface for both of these situations, however, it's a little harder than simply replacing Widget with IWidget in the controller and the view. Somehow MVC has to figure out which implementation to use with IWidget. This is where a little creativity and dependency injection can come in handy.

Internally, MVC uses a class called the DefaultModelBinder to reconstitute the strongly-typed view model from the posted form data sent via the POST. This class's "BindModel" method is called from the controller, and the type expected by the controller's action is passed as a property of the ModelBindingContext parameter expected by BindModel. There's a whole slew of things BindModel goes through to bind the form data onto the model, but the only thing we're interested in at the moment is how it creates an instance of an object based on a specific type. Turns out this is accomplished via the "CreateModel" method.

Now, the CreateModel method is very simple. Ultimately, unless the type expected is an IDictionary<T> or an IEnumerable<T>, it's going to simply make a call to Activator.CreateInstance, pass in the type requested, and return the result. Luckily for us, CreateModel is virtual, so we can override it with something like this:

public class DependencyInjectionModelBinder: DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
object result;

// Attempt to get the value from ObjectFactory. If this fails
// use the default method of constructing the model.
if ((result = ObjectFactory.TryGetInstance(modelType)) == null)
result = base.CreateModel(controllerContext, bindingContext, modelType);

return result;
}
}

Next, we need to ensure this new model binder is the default model binder. To do this, we need to simply add this line in our global.asax.cs file, somewhere where it will be called from Application_Start():

ModelBinders.Binders.DefaultBinder = new DependencyInjectionModelBinder();

So, now we have a DependencyInjectionModelBinder registered as our default model binder for our application. Now, supposing that Widget implements IWidget, and you want your view and controller to rely on IWidget instead of Widget, you need to ensure you've registered Widget as the default concrete class of IWidget. Add this line so it will be called from the Application_Start method in the global.asax.cs file:

ObjectFactory.Configure(x => x.For<IWidget>().Use<Widget>());

Lastly, we need to change our view and our controller to use the interface instead of the concrete class:

View:

<%@ Page Title="" Language="C#" MasterPageFile="../Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<MyNamespace.IWidget>" %>

Controller:

[HttpPost]
public void Details([Bind] IWidget widget)
{
// do something with the widget.
}

That's it! You've now made MVC more SOLID by adhering to dependency inversion principle in your controller and your view. Nowhere in your application, other than when registering Widget as the default implementation of IWidget, should you need to directly reference Widget as a concrete class.

Sunday, March 14, 2010

"Value types do not support variance".... but why?

I was reading over the details regarding the new way the IEnumerable<out T> interface is implemented in C# 4.0, specifically as it relates to covariance and contravariance. In my reading, I came across this interesting statement:
"Value types do not support variance."
What does that mean?

Certainly, the following code compiles just fine:
IEnumerable<object> strings = new List<string>();
But the following does not:
IEnumerable<object> ints = new List<int>();
Nothing more is stated in the documentation, other than that variance is not supported on value types. So I got to thinking... why is this?

Here's my theory (and I'd love to have someone from Microsoft verify this for me). When you're converting the List<string> to an IEnumerable<object>, you're not actually changing the real essence of what your original List<string> is. Internally, the List<string> class is going to still hold on to an array of references to objects on the heap, but instead of specifying that those objects on the heap are strings, we simply say they're objects instead. An array of value types, however, is different. The array of integers is a block in memory that holds the actual values, whereas the array of strings is a block of memory that holds references to the actual values, which are residing somewhere else on the heap. So what would have to happen in order for the covariance to work? Basically, an entirely new list would have to be created, and each value that resides in the internal array supporting List<int> would have to be boxed and the boxed reference placed in a new array, thus creating an entirely new List<object> to support this kind of thing.

This would be more than simply a performance hit. This would require every implementor of IEnumerable<out T> to define an "escape route" so to speak for when the type of T is a value type. So, instead of jumping through hoops to support this scenario, it's simply disallowed.

Thursday, February 18, 2010

Should ArrayList Be Deprecated?

I came across a poster on the Microsoft Forums the other day, who was asking a question about the .NET ArrayList. Whenever I hear the word, I cringe. Since the advent of generics, I've never once used ArrayList.

That being said, some of you may have, so I'd like to hear what you think.

Should ArrayList be deprecated, perhaps with a deprecation message that points the user to List?

Feel free to vote in the poll on the right, or leave your comments below.