One of the features (or challenges) with ASP.NET MVC is the number of approaches you can use to successfully implement a feature. Take the "log each action method" requirement. One possible approach is to use a custom action invoker, however, an action invoker already has enough responsibilities (see Jonathon's post), and also has a fairly low level API. A custom action filter is the better choice.
public class LogAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { // ... log stuff before execution } public override void OnResultExecuted(ResultExecutedContext filterContext) { // ... log stuff after execution } }
You can use the LogAttribute on every controller.
[Log] public class VuvuzelaController { public ActionResult Detonate() { // ... } }
After you've added the attribute to a few controllers, you might start wondering why you don't use a base class instead. After all, the Controller class defines virtual methods to hook into the pre and post action execution, and with this approach you won't see the [Log] attribute on every controller.
public class LoggingController : Controller { protected override void OnActionExecuting(ActionExecutingContext filterContext) { // ... log stuff before execution } protected override void OnActionExecuted(ActionExecutedContext filterContext) { // ... log stuff after execution } }
Now everyone just needs to use LoggingController as a base class.
public class VuvuzelaController : LoggingController { public ActionResult Detonate() { // ... } }
Wait! What if someone forgets to use the base class? This problem is easy to prevent - we'll look at the details in a future post (think unit test with LINQ queries and reflection).
The bigger problem is all the design issues a base class brings into play. What if someone else overrides the methods and forgets to call the base implementation? What if someone wants to manage transactions and ORM sessions in a base controller? If you follow the pattern you either end up with a fragile class hierarchy or a SessionInitializingTransactionMonitoringLoggingCachingSuperDuperController base class.
Fortunately, ASP.NET MVC finds and respects the action filters on a base controller class. You can add additional functionality without adding implementation.
[Log] public class FootballController : Controller { }
Now every controller derived from FootballController will log.
public class VuvuzelaController : FootballController
{
public ActionResult Detonate()
{
// ...
}
}
“Is the software installed?” asked my friend Kenny Cole.
“Yes”.
Our eyes tracked the mouse on screen as I right-clicked on a project and picked Open With –> Expression Blend 4. Diodes flickered. Hard drives whirred. A dialog box appeared.
Kenny’s angular face raised an eyebrow. “A modal dialog ... interesting start,” he said.
“Yes,” I replied with a nervous laugh, “well, this only happens once in a blue moon”.
I moved the mouse and clicked Close as fast as possible. Diodes flickered. Hard drives whirred. A dialog box appeared.
I could see the frown on Kenny’s face out of the corner of my eye. “Maybe you should click that link to install an S – D - K and make the software happy,” he said.
“Oh, sure”, I replied. I clicked the “Install the Expression Blend SDK for Silverlight 4” link. Diodes flickered. Hard drives whirred. A browser appeared.
“It doesn’t appear to be installing anything”, Kenny observed.
“No, no it’s not”.
“It appears to be an ad for Microsoft Office”, Kenny said.
“Well,” I replied, “it’s the home page of the Microsoft Download Center. Presumably from here you can search for SDKs”.
“And then ... presumably ... you can download and install them one by one?”, he asked.
“Ah ... right. Let’s just come back and do that later. I’m sure it’s ready to start now”. I flipped back to Expression and clicked the Close button. Diodes flickered. Hard drives whirred. A dialog box appeared.
Kenny’s frown turned to a scowl. “Three times!”.
“Yes, well, it’s a security warning,” I said while clicking the Yes button. Diodes flickered. “I mean, you can’t be too secure these days, right?”. Hard drives whirred. “It’s just being cautious, you know”. A dialog box appeared.
Kenny stood up, and started walking towards the door.
“Wait!” I cried - “check out these rounded corners!”.
At least not from the reflection API's point of view.
For example, given the following class ...
public class Wig { public Wig(string name = "Angry") { // ... } }
... you can successfully instantiate the class as if it had a default, parameterless constructor:
var wig = new Wig();
However, trying to do this via Activator will fail:
var wig = Activator.CreateInstance<Wig>(); // MissingMethodException: // No parameterless constructor defined for this object.
Which makes sense once you realize optional parameters are compiler sugar, and the following code will clearly show a single constructor requiring a single argument.
var ctors = typeof(Wig).GetConstructors(); foreach (var ctor in ctors) { Console.WriteLine(ctor.Name); foreach(var param in ctor.GetParameters()) { Console.WriteLine("\t{0} {1}", param.Name, param.ParameterType.Name ); } }
Scenario: You want to know if a client completes a large download successfully.
Possible Solution: Create a custom ActionResult (perhaps derived from FileStreamResult) that streams the data and checks to see if the client remains connected.
public class CheckedFileStreamResult : FileStreamResult { public CheckedFileStreamResult(FileStream stream, string contentType) :base(stream, contentType) { DownloadCompleted = false; } public bool DownloadCompleted { get; set; } protected override void WriteFile(HttpResponseBase response) { var outputStream = response.OutputStream; using (FileStream) { var buffer = new byte[_bufferSize]; var count = FileStream.Read(buffer, 0, _bufferSize); while(count != 0 && response.IsClientConnected) { outputStream.Write(buffer, 0, count); count = FileStream.Read(buffer, 0, _bufferSize); } DownloadCompleted = response.IsClientConnected; } } private const int _bufferSize = 0x1000; }
You can now log or audit the DownloadCompleted property of the result from an action filter. I'm sure this isn't 100% foolproof, but it seems to work reasonably well. Note: The IsClientConnected property of the response only tells the truth when running under IIS. With the WebDev server the property always returns true.
There are a number of ideas to chew on in this 6 year old paper by Terence Parr: "Enforcing Strict Model-View Separation in Template Engines". Parr is one of the creators of the StringTemplate template engine, which is ".. as simple, consistent, and powerful as possible without sacrificing strict model-view separation".
One of the highlights is the list of 5 rules implied by strict separation. Parr introduces them like so:
Before formalizing my intuition, I kept a few simple rules and tests in mind to evaluate entanglement. Could I reuse this template with a completely different model? Could a designer understand this template? 1f it looks like a program, it probably is. If the template can modify the model, it’s part of the program. If order of execution is important, it’s part of the program. If the template has computations or logic dependent on model data, it’s part of the program. If types are important, the template is a program.
The idea behind a strict separation is to avoid programming in the view and merely apply templating. With the web forms view engine in ASP.NET MVC we can not only break all 5 of Parr's rules, we can crumple them up like tissue paper and light them on fire. Of course, we can also behave ourselves and follow the rules of separation- or at least all of the rules except the one stating "the view cannot make data type assumptions".
Most MVC developers I talk to prefer to use strongly-typed views. Strong typing gives us intellisense and compiler errors. But - is there a cost? Does strongly typing the view lead to a form of bad coupling between the view and the model? Does it make the view harder to change? Does it make the designer's job more difficult?
I can hear you saying "no". Is this because the rendering phase of the processing pipeline is nearly impossible to test? Would it change your mind if testing a view template was easy?
ActionResult result = controller.ChangePassword(); var output = result.ExecuteResult();
What do you think?
I was in Oslo, Norway the week of June 14th for the Norwegian Developer’s Conference.
One of the recurring side conversations I had at this conference centered around the contrast between a vendor led conference and an independent conference (like the NDC). If you’ve only been to vendor conferences then you owe it to yourself to experience the amazing depth and breadth of the entire .NET world. Sure, the NDC had sessions on the latest bits from Window Azure and Windows Mobile, but the following topics did not fall into the minority:
The essence of a developer conference forms when the organizers sit down and frame the guiding question for their mission. Is the question going to be:
What does a developer need to be a well-rounded and skilled professional?
Or is it:
What should we tell attendees about our products?
In any case, videos from NDC are coming online at streaming.ndc2010.no - check it out for yourself.
If you are visiting Oslo I can highly recommend a walk through the Vigeland Sculpture Park (if the weather isn’t overbearing). Here is a picture I took near the Wheel of Life looking ESE towards the Monolith.
Years ago, when I began to take JavaScript seriously, I thought prototypical inheritance was marvelous.
Accordian = function (element, isOpen) { this.element = element; this.isOpen = isOpen; } Accordian.prototype = { open: function () { // ... }, close: function (value) { // ... } };
It took some time, but eventually I wondered if using an OOP approach in page scripts was like using cranes to tow compact cars.
It turns out the developer working on a single page doesn't need to build or consume big, strong abstractions for programming "in the large". It's become clear over the last several years that function() is the right-sized abstraction. You find the DOM element and I'll tell you how it should behave. You wire up the click event and I'll tell you what to do when it's clicked. Give me functions that figure out the hard stuff and let me fill in the details.
$(".header").click(function () {
$(".content", this).slideToggle();
});
This doesn't mean there isn't value in modifying prototypes, but I think the prototype approach has a greater value in building libraries, APIs, and core infrastructure pieces. For page scripts the ability to program against a functional API allows a developer to separate concerns in a concise manner, and compose complex behaviors using smaller pieces of raw material. Sometimes these pieces of functionality even carry state, but the state is wholly encapsulated in a closure.
But will it hold?
There is a lot of change on the horizon as features like background processing, local storage, and a drawing canvas will come with the browser for free. Will the language and paradigms support programming in the large?
What do you think?