I went to a general store, but they wouldn't let me buy anything specific. - Steven Wright.
One of the challenges of working with a general purpose programming language (like C#) and a general purpose framework (like ASP.NET, Silverlight, or any other application framework) is building something specific.
The trick is to apply constraints and build abstractions to take ownership of the situation, and there is an entire bag of fancy tricks to pull from, like using fluent APIs or applying domain driven design. But taking real ownership goes beyond just building the right classes - it's also about circumventing the underlying framework to match the needs of the team, the application, and the business in general. If you look at opinionated frameworks like OpenRasta, FubuMvc, and Caliburn.Micro, you'll find they make assumptions about what you want from a framework. These assumptions simplify design decisions and you'll often find it easier to find the one right way to achieve some goal.
Let's say your are using a relatively general purpose web framework like ASP.NET MVC 3. Every controller action needs to respond with one of two views depending on some bit of user information. To simply the scenario, we'll say we switch on the IsAuthenticated flag.
if (Request.IsAuthenticated) { return View("private/index"); } else { return View("public/index"); }
Although it feels pretty good to keep the different views you need inside of separate folders, it's just a first step. The above code typifies what happens when you let the framework control you instead of taking control of the framework. When the above code appears inside of every primary controller action it reads like this: "the framework gives me a View method, and that's all I have to use."
To take this one step further you'll want to simplify the controller code and move the view decision out of the controller. Maybe this means you write a new helper method, a new action filter, or if the logic applies everywhere, a new view engine.
public class CustomViewEngine : IViewEngine { ... public ViewEngineResult FindView( ControllerContext controllerContext, string viewName, string masterName, bool useCache) { if (controllerContext.HttpContext.Request.IsAuthenticated) { viewName = "private/" + viewName; } else { viewName = "public/" + viewName; } return _inner.FindView(controllerContext, viewName, masterName, useCache); } RazorViewEngine _inner = new RazorViewEngine();
... }
Then configure the engine into your application.
protected void Application_Start() { ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(new CustomViewEngine());
... }
And now controller code is simple again.
return View();
The need for your application to offer two distinct views in every scenario is now implicitly baked into the infrastructure.
Taking control of the software often means you apply constraints (what I should not do) and conventions (what I don't need to do). The downside is having more implicit "magic" inside the code, but the upside is having simple code focused on specific solution.