Yesterday we had an electrifying event “Open Source in .NET | Open Day” in collaboration with Microsoft Bangladesh, that I have posted earlier about. In my second session I unveiled my shiny new Open Source project “NoBrainer” which is the topic of this post. The presentation can be viewed here:
NoBrainer is an MVC + CMS Framework, as its name suggests for low-fi developers and savvy business stakeholders. It provides developers the flexibility of MVC as well as the control of WebForm, resulting in a testable and content manageable WebForm infrastructure for you application. NoBrainer currently supports only Web at the launch, however it can easily be extended to work with Desktop as well as Mobile. That way your logic and test code remain the same across different UI layers. Take time and flip over the slides. You will get the understanding what I have tried to achieve with this project. This Framework is used by one of the largest Enterprises and it is serving several millions of their users pretty well.
The Motivation
Converting your WebForms project into ASP.NET MVC is not always a suitable option for you. There is a huge cost associated with conversion, which is in ideal case totally dependent on decision from business stakeholders. On the other hand, if you are one of the business guys and you maintain a pool of developers for your project, chances are that you may want to convert your project into an ASP.NET MVC project. But, when your developers become reluctant to change, and to learn a whole new paradigm of development, you have no choice but stick to the old WebForm, until you use NoBrainer. So, you need a Framework that slowly guides your developers to the MVC practice without sacrificing the convenience of rich and drag & drop designer of WebForms.
Note: This Framework addresses a particular need – it does not necessarily replace the ASP.NET MVC in all aspects.

Figure: Data validation and automatic error binding: CMS-driven MVC Framework in action
The project itself is easy to setup and the Sample is concise, so that one who wants to learn can follow easily. This step-by-step walkthrough is also included (as you can see in the Documentation part) in the project.
Note: This project assumes that you have general idea about Model-View-Controller pattern.
Easy Setup
NoBrainer takes a novel approach – WebForm is totally rethought to use with MVC. Follow the steps:
(1) Reference NoBrainer.dll in your web project.
(2) Add the following in your web.config:
<appSettings>
<!-- Any folder path you want. A folder inside your web project is preferred. -->
<add key="CmsXmlDirectory" value="D:NoBrainerNoBrainer.SampleContent"/>
</appSettings>
(3) Add this to your Global.asax:
void Application_Start(object sender, EventArgs e)
{
// Ignite NoBrainer engine.
Engine.Boot(() => new NoCache(),
ConfigurationManager.AppSettings["CmsXmlDirectory"], "xml", "html, htm");
}
Done! Now you are free to choose which WebForm inside your project, you would prefer to convert to MVC pattern.
NoBrainer makes it a breeze to make your WebForm CMS-driven MVC style
Magic of NoBrainer is that you can very slowly start converting your project into MVC. You can take one WebForm at a time. Let us see how we can implement MVC in our Login.aspx page. Follow the steps:
1. Model
Let’s create a Model for our MVC that will be used to hold necessary data to transfer back and forth from and to View and Controller. Notice that the Model was derived from ModelBase which is the base class from NoBrainer to indicate that the class which extends it is actually a Model.
public class AccountModel : ModelBase
{
public string UserName { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public bool PersistentCookieEnabled { get; set; }
}
2. View (HTML)
Now let’s create the View. Notice that the HTML tags here have several highlighted non-standard attributes. Some of them matches the properties of the Model that we created earlier. Ex. UserName, Password, PersistentCookieEnabled. NoBrainer automatically maps these Model properties without requiring any hand written code.
<!-- Example: Model(you can also say View)-wide common error message -->
<span runat="server" class="failureNotification" Model="ErrorMessage"></span>
<fieldset class="login">
<legend>Account Information</legend>
<p>
<asp:TextBox runat="server" ID="UserName" CssClass="textEntry" Model="UserName">
</asp:TextBox>
<!-- Example: automatic error binding from Model to View -->
<span runat="server" class="failureNotification" Model-Error="UserName"/>
</p>
<p>
<asp:TextBox runat="server" ID="Password" CssClass="passwordEntry" TextMode="Password"
Model="Password"></asp:TextBox>
<!-- Example:
1. HTML-attribute level CMS capability
2. Show/hide CMS driven error message by Cms-Error
-->
<span runat="server" class="<%# Cms["spnPasswordError"].CssClass%>"
Cms-Error="Password">
<!-- Example: strongly typed Model -->
<%# Cms["spnPasswordError"].Content %>
</span>
</p>
<p>
<asp:CheckBox runat="server" ID="RememberMe" Model="PersistentCookieEnabled"/>
<asp:Label ID="RememberMeLabel" runat="server" AssociatedControlID="RememberMe"
CssClass="inline">Keep me logged in</asp:Label>
</p>
</fieldset>
Model-Error is an amazing feature that automatically maps error message sent from Controller to the UI. For example, if user has typed wrong password, NoBrainer will find HTML element having Model-Error = “Password” and update its content to the error message associated with Password.
Cms is a page level dictionary which allows you to put CMS content which come from XML (we will cover that in a while).
Our Model did not have ErrorMessage property. Where did it come from? Well, No Brainer provides bunch of really useful extensions and default properties to help you write MVC friendly WebForm. ErrorMessage is one of them, which contains View-wide generic error message occured. For example: Incorrect username/password combination.
As you can see there is no MVC specific change in WebForm design. You can use your same old WebForm practice, drag and drop controls from the Toolbox, use design-view, etc. whatever you like. However, to make automatic control mapping work, you will have to map Model properties. Even that’s optional too, if you prefer to do that WebForm way. Example: model.UserName = txtUserName.Text in your code-behind file. NoBrainer gives you absolute freedom.
3. View (code-behind)
Let’s take a look at the code-behind of this page. First thing you are going to notice is that the View does not inherit System.Web.UI.Page, rather it does from ViewBase which is the base class for Views provided by NoBrainer. It takes two parameters. First is the Controller name which has the driving logic for this View and the Model type that the Controller and View both will be dealing with. We have declared AccountModel earlier, but AccountController (our Controller) will be implemented in the next step.
public partial class Login : ViewBase<AccountController, AccountModel>
{
protected void Page_Load(object sender, EventArgs e)
{
if(IsPostBack)
{
InvokeController(v => Controller.Login(Model));
if(Model.IsValid)
{
FormsAuthentication.RedirectFromLoginPage(Model.UserName, Model.PersistentCookieEnabled);
}
}
else
{
if(Request.IsAuthenticated)
{
Response.Redirect("~/Account/Manage.aspx");
}
}
}
}
You will also notice that there is a method named InvokeController which is a native NoBrainer method inferred from ViewBase. This method allows you to map View => Model and pass that Model to the designated method you have written in your Controller (in here: Login is a method written inside AccountController. We will see that in a while.). Then the Controller method performs some actions (such as Validation, etc.) and returns the modified Model to the View. The View (web layer/WebForm) then examines the Model and decides what to do next. That way we leave NoBrainer to do the mapping work, and seperate the logic to our Controller.
4. Controller
Now that our Model and View are complete, let’s talk about Controller. We will talk everything via interfaces. Why? Because one of the advantages of using interfaces is that it will allow us to make the Controllers unit testable.
public interface IAccountController
{
AccountModel Login(AccountModel user);
}
public class AccountController : ControllerBase, IAccountController
{
public AccountModel Login(AccountModel user)
{
/*
Example: how Models can be validated and
error messages can be sent from Controllers.
*/
// Example: how error messages can be set from the Controllers
if (string.IsNullOrEmpty(user.UserName))
user.ErrorMessages["UserName"] = "UserName cannot be empty.";
// Example: how to enable/disable CMS based error messages to the UI
if (string.IsNullOrEmpty(user.Password))
user.CmsErrorMessages["Password"] = true;
if(string.IsNullOrEmpty(user.UserName)
|| string.IsNullOrEmpty(user.Password))
{
user.ErrorMessage = "There are errors. Please fix them and try again.";
}
else
{
if (!Membership.ValidateUser(user.UserName, user.Password))
{
user.ErrorMessage = "UserName/Password combination is incorrect.";
}
else
{
FormsAuthentication.SetAuthCookie(user.UserName, user.PersistentCookieEnabled);
user.IsValid = true;
}
}
return user;
}
}
First thing you are noticing here is that it inherits ControllerBase as well as IAccountController. AccountController has to implement the method (for our case: Login) that we called earlier from View. As you can see it takes an instance of AccountModel, which is later validated whether there’s any error message and then returned the same object in the end after necessary modification.
If you need to set an error message specific to certain property (in here: UserName) of the Model, you should use the Model’s in-built dictionary ErrorMessages to do that. However, should you require to make the error message CMS driven, in other words, your business stakeholders want it to hand-written by themselves outside the application, you should use the other dictionary named CmsErrorMessages. You will create a CMS collection which we will discuss in the last step of this tutorial, that should hold a text representing user.CmsErrorMessages["Password"]. What this piece of code will do is it will turn on/off the text’s visibility (recall: Cms-Error=”Password”). We will look at that later.
Now about the Membership.ValidateUser and use of FormsAuthentication. You can use them. That’s fine! But, if you would like to make your MVC to work across different form factors of computing devices such as Desktop/Mobile, you will have to move it to web layer or plug it from somewhere else to make the Model and Controllers device independant. Because these classes belong to ASP.NET which you do not want to use in Desktop/Mobile.
5. CMS XML
That’s it! Oh, one last thing, because this View (as we have written) requires us to define a CMS variable “spnPasswordError” (see View HTML) for this particular View, we need to define it inside our Content (move to the beginning of this document where we defined CmsXmlDirectory) folder. Let’s create TanzimSaqib.NoBrainer.Sample.Controllers.StaticPageController.xml, which matches the Controller namespace we have in this project. This namespace mapping is important. Put the following XML inside of that.
<?xml version="1.0" encoding="utf-8" ?>
<!-- Example of content injection aka CMS capability -->
<Views>
<View pageName="AccountLogin.aspx">
<Content id="spnPasswordError" cssClass="failureNotification">
Password cannot be empty.</Content>
</View>
</Views>
As you can see this XML file contains all possible Views that this Controller can handle. For our example of Login screen, we are specifying our View page path as AccountLogin.aspx, inside of that we are defining our CMS variable spnPasswordError, its cssClass as well as the content. These data are modifiable as CMS data as well as injected while rendering the page by NoBrainer.
Figure: Open Source in .NET | Open Day, at which NoBrainer was launched
Conclusion
That way you make your logic seperate from the UI layer, which also makes it unit testable, and deployable to many different form factors of computing devices. It also facilitates the power of CMS. You do not have to give business stakeholders full freedom on your code. Just give them FTP access to the Content folder if you will, and they will take it from there. They will never call you up at the dead night to make some changes to the content just because that’s important to their business, rather they could do it by themselves.