Category Archives: MVC

Asp.Net Core: Building TypeScript with Gulp

In the preceding post I show how you can compile your TypeScript files with tsc. You can also simply use Visual Studio and enable the option: “Compile TypeScipt on Build” in the Build properties of your ASP.NET Core project.
While these are valid options I generally prefer to use Gulp to setup my own workflow.
In this post I’ll explain how you can use Gulp to setup and fine tune a Typescript build.

The code is available at: https://github.com/geobarteam/myClub/tree/1-3.Build.TypeScript.With.Gulp

$ git pull https://github.com/geobarteam/myClub.git 1-3.Build.TypeScript.With.Gulp

A) Pull the Gulp packages

First we’ll need to use npm to pull down some gulp packages that will help us in setting up our Typescript build.
The npm packages we need are:
“gulp-rimraf”: “^0.2.0”,

“gulp-sourcemaps”: “^1.6.0”,

“gulp-typescript”: “^2.13.4”,

“gulp-uglify”: “1.2.0”,

“rimraf”: “2.2.8”

gulpfiles1

To install these packages you can copy paste the list here above right under the “devDependencies” of the package.json file but I prefer to use npm.
To install these packages through npm, first navigate to the root of your project and use the command:
$ npm install [packagename] –save-dev
The –save-dev option is used to update our packages.json and put the new dependencies under the “devDependencies”.

B) Create the Gulp Tasks

Here we’ll use gulp-typescript to compile automate the typescript build. The documentation can be found here. Open the gulpfile.js and modify as follows:

  1. Require the packages
    [snippet id=”221″]
  2. Add following path variables
    [snippet id=”231″]
  3. As we’ll use the tsconfig.json file to set the build properties we’ll create a project:
    [snippet id=”241″]
  4. Write the typescript build task
    [snippet id=”251″]
  5. Write the clean tasks
    [snippet id=”261″]
  6. If you use VS2015 you can bind the gulp tasks with your VS build. The clean tasks should run before our build and after our build we should run first the ‘tsbuilld’ and then the ‘min’ task.
    gulpfiles2gulpfiles3
  7. Write a watch task if you want that the ts build is triggered when a ts file changes.
    [snippet id=”271″]
  8. If needed, modify your angular script links. As the predefined min:js gulp task minifies all js files we can choose to use the minified app.js file in production.
    [snippet id=”281″]

ASP.NET Core: Modify an Angular App to use Typescript

In this step we’ll modify our Angular 1.x app to use typescript.

Code can be found on: https://github.com/geobarteam/myClub

Branch: 1-2.Add.Typescript

Configure a Typescript Build

First install the latest Typescript compiler:

npm install -g typescript

Add a script folder to your project, this will be the home for your typescript files:

scriptfolder

Add a tsconfig.json file right under the scripts folder.

tsconfigs

The tsconfig.ts file contains the build options for the typescript compiler, it allows to run the Typescript compile process without specifying arguments.  To compile from the command line you should navigate inside the script folder and run: tsc

tsconfig.ts:
[snippet id=”181″]

Configure Typings

Typings allow to simply manage and install TypeScript definitions.

Install typings globally via npm:

npm install typings –global

To install typing definitions for angular 1.x, and Jquery use the command:

typings install angular jquery –ambient –save

 

Migrate the Angular scripts to use typescript

Move all your javascript angular scripts into the “scripts” directory and rename their suffix to “xxxxx.ts”.

tsconfigs

Add the “any” type to all your parameters and variables.
This is only required because we   used the “noImplicitAny”: false setting.  I like to use it as it makes clear that I should type all variables.
[snippet id=”191″]

You should now modify the tsconfig.ts to add the files to compile and set an output file:
[snippet id=”201″]

Inside _Layout.cshtml modify the angular scripts link to use the app.js script file:
[snippet id=”211″]

When compiling the app in VS2015 or running tsc from the command line inside the script folder;  you should end up with a app.js file inside the wwwroot/app directory:

prscr

Test the app, it should still run exactly as before.

ASP.NET Core: Create an Angular App


aspnetcore

Here I’ll start a series of little tutorials on how to build from scratch a new Angular application with ASP.NET Core and MVC5.

The code used in these tutorials can be found under my git hub project: myClub

Every post has his own dedicated branch, so you can just use  “git checkout <branchname>” to find all the source code of the post you’re interesting in.

1) Create a new MVC core project

To generate a new project we’ll use Yeomen.  Open a commend prompt/bash shell or powershell shell on the location you want to put your new project =>

mkdir myClub

cd myClub

yo aspnet

yeomen

Choose Web Application Basic and provide a name.


 

2)          Add Angular to the MVC app

1)      Add Angular, from command line inside you project dir:
bower install angular angular-ui-router –save

Add angular to _layout.cshtml :

<script src=”~/lib/angular/angular.min.js”></script>


 

3) Create two js scripts under wwwroot:

app.config.js

[snippet id=”111″]

app.module.js

[snippet id=”121″]

printscreenvscode


 

4) Add two controllers

myTeam.controller.js & home.controller.js

[snippet id=”131″]


 

5) Add the two html pages inside the angular app

home.html

[snippet id=”141″]

myTeam.html

[snippet id=”171″]


 

6)  Reference the js scripts in _layout.cshtml

[snippet id=”151″]


 

7)      Add the angular app inside idex.cshtml and move the navbar from _layout.html

index.cshtml

[snippet id=”151″]


 

8)      Test the application

testpage

Asp.Net Core: How to configure your development environment on Linux

linuxVsCode

In this post I explain how to setup an ASP.NET 5 RC1 development environment on Linux Ubuntu 14.04 using vagrant.

Here you’ll find the vagrantfile and the provisioning script used to setup your vagrant box.

You can also use the provision.sh script to configure an Ubuntu 14.04 VM without using vagrant. Nevertheless I strongly recommend to use vagrant and to follow the steps depicted here.

Install Vagrant on your windows host

  • First download and install chocolatey: https://chocolatey.org/
  • Install Cygwin and add the openssh through cyg-get, open a command or powershell pompt and type following commands:

choco install Cygwin

choco install cyg-get

cyg-get openssh 

  • Setup Vagrant and virtualbox:

choco install virtualbox

choco install vagrant

  • Test vagrant is installed correctly, Open a Cygwin terminal and type:vagrant –v

The console should output your vagrant version.  If this isn’t working check your path variable, the bin directory of vagrant should be added, if this is not found, add the vagrant bin dir to your Path.  (It’s usually: C:\HashiCorp\Vagrant\bin)

 

Download and setup your Linux base box with Vagrant

  1. Open a Cygwin terminal and type:

$ mkdir data

$ mkdir netCoreRC1

$ cd netCoreRC1

 

  1. Configure and launch your vagrant box
    Copy the ”Vagrantfile” and “provision.sh” into the directory netCoreRC1 – you’ll find these files here: https://github.com/geobarteam/vagrant/tree/master/netCoreRC1

Now run:

$ vagrant up

This will start your vm and start the provisioning process.

To stop or reboot your vm:

$vagrant halt

To login into your box through ssh:

$ vagrant ssh

If you need to reconfigure your keyboard for your country type:

sudo dpkg-reconfigure keyboard-configuration

It’s also recommended to create your own user with:
sudo adduser [your user name]
and add it to the sudo group:
sudo adduser [your user name] sudo

You can login in the gui interface with user: vagrant and password: vagrant.

To complete the install you’ll need to reboot your vm: vagrant reload
To test that .net core and mono are installed open a terminal or ssh session and type:

dnvm list

You should see something like:
Active Version              Runtime Architecture OperatingSystem Alias

—— ——-              ——- ———— ————— —–

1.0.0-rc1-update1    coreclr x64          linux             *

1.0.0-rc1-update1    mono                 linux/osx       default

If something went wrong or you want to use another user than vagrant, open an ssh session under the new user account:

ssh -p 2222 [username]@localhost

Copy paste following commands into the ssh window:

curl -sSL https://raw.githubusercontent.com/aspnet/Home/dev/dnvminstall.sh | DNX_BRANCH=dev sh && source ~/.dnx/dnvm/dnvm.sh

dnvm upgrade -r coreclr

dnvm upgrade -r mono

This should install .net core & mono and fix your box, if you still encounter errors you can find the detailed install procedure of .net core under:
https://docs.asp.net/en/latest/getting-started/installing-on-linux.html

Install Visual Studio Code

Go to: https://code.visualstudio.com/ and download Vs Code.
You’ll find the install instructions under: https://code.visualstudio.com/

Once you’ve rebooted your dev box you can start experimenting with asp.net 5.

A good point to get started is to use yo men (it was already installed through the provisioning step) to create an empty Asp.Net 5 app as explained here: https://azure.microsoft.com/en-us/documentation/articles/web-sites-create-web-app-using-vscode/

Have fun!

 

Setup, develop and deploy your ASP.NET MVC Umbraco website on Azure

To run Umbraco on Azure you can choose to use the build in Azure template.

pic1

 

For simple websites this could do the job but if you want to extend the portal with your own code or if you want to version control your site you’re better of starting with a blank MVC website.

 

Umbraco development lifecycle

Managing the lifecycle of an Umbraco application is somewhat challenging as Umbraco is one platform made out of several components (code/DB/content) and it’s not always clear what you need to deploy to promote content or features.  Especially deploying database changes can be cumbersome.  I personally chooses to avoid to have to stage DB changes by running all my environments (local/integration/production…) on a single DB hosted on Azure.

Because Umbraco already has a notion of staging you can for most cases work safely on the production database from your local machine without fearing to impact production.   Nevertheless when I need to make risky changes to my application or when I need to test a major Umbraco upgrade then I setup a clone of my production DB and do the development and testing on the clone.

For most of the changes my development cycle goes as follow:

  1. All my changes are made locally through the local umbraco portal (running on my local machine) or for Extensions through Visual Studio.
  2. When new content is added to the site I make sure these are included in my local Visual Studio project.
  3. I make sure that everything run nice locally.
  4. I check-in all the changes
  5. Publish the changes to Azure through the publish wizard.
  6. Test that everything runs fine in production.
  7. Promote the content once everything is tested

Umbraco first deployment

In this part I explain the steps to take to deploy the skeleton of an empty ASP.NET MVC Umbraco website.

Through the Azure portal:

  • Create a SQL server DB, don’t forget to note your password!
  • Create a new web app

Open VS: Start new project, Web, Asp.Net web application

Manage Nuget packages, umbracoCms

 

pic2

 

Click RUN in VisualStudio and launch the website locally.

Enter you Name, email & password and click Customize.

As Database type choose Microsoft SQL Azure.

You can find your connection details from the Azure portal (via old portal): select your DB, dashboard, Show Connection strings.
Use the ADO.NET connectionstring, copy each relevant part in the textboxes, for the server you need to provide the “server” part but without the leading “tcp”.
Click next.

Before publishing your website to Azure you first need to include files/folders to your project:

  • App_browsers
  • App_Plugins
  • umbraco
  • umbraco_client
  • config\splashes
  • css
  • js
  • media
  • scripts
    If you used a template also include the cshtml files under the Views folder.

 

pic3

Also set the build actions of the following web.config files to “none”.

– \umbraco\install\web.config

– \umbraco\Xslt\Web.config

 

pic4

Now publish the website: right-click your web project, choose publish.
Select your Azure Web App, the connectionstring should be retrieved by VS from your project.

 

If you followed everything in the exact order you should see your website running on Azure!

ASP.NET MVC with Webforms

source code can be found here 

It’s now generally admitted in the community that Unit testing and TDD (Test Driven Development) are valuable techniques when it comes to increasing the overall quality of our code. Nevertheless unit testing can be costly especially when you’ve applications with a lot of logic implemented in the UI. Therefore if we want to make our application testable we need to separates the UI from the rest of the application.

Martin Fowler described on his site some patterns that separate and diminish the UI logic to a bare minimum. They are all variants of the classical MVC (Model View Controller) pattern. The MVC split the application in 3 parts: the view handles the display, the controller that responds to user gestures and the model that contains the domain logic. The MVC is the foundation of very popular portal frameworks like Ruby on Rails.

To build web sites applying the MVC pattern with .Net developers can choose among several MVC frameworks like Monorail or the new ASP.NET MVC. In anyway, MVC frameworks like ASP.NET MVC are based on completely different paradigm as the ASP.NET Webforms framework. This means that you have to re-learn to program web apps from scratch. Another setback is that there are no ways to refactor your old ASP.NET applications so that they can fit into the MVC framework. I want to make myself clear, I believe that frameworks like Monorail or the coming System.Web.MVC are the future way of programming web apps in .NET but it demands a considerable amount of effort to learn new frameworks. It’s difficult for someone like me who has invested lots of years in mastering the classical ASP.NET code-behind model to re-learn everything from scratch. In the meantime this should not be an excuse to not make my code more testable.

In this post I will explicit through a simple example how to use the model view controller pattern on top of the code-behind model. We will create a login form with the MVC pattern.

Setup your solution

Create a new solution “Zoo” with 3 projects –>

  • ZooWebsite-> ASP.NET web appplication
  • ZooLibrary -> Class library 
  • ZooTest – Class library
  • Create a reference from ZooWebsite to ZooLibrary

    (ZooWebsite , add reference, project tab select ZooLibrary)

  • On ZooLibrary add a reference to System.Web

ne project

 

The View

To make our code testable it’s very important to be able to decouple the UI from the ASP.NET code-behind.  Therefore we will create an interface our ASP.NET page should implement.  This View interface will represent the contract the UI as to conform to.  When we will test our controller we will not do this with our actual web page but through a mock object that implements the View interface.    

Add an interface ILoginView on the project ZooLibrary:

   1:  namespace ZooApplication.Library
   2:  {
   3:       public interface ILoginView
   4:      {
   5:          string ErrorMessage { get;set;}
   6:          string EmailAddress { get;set;}
   7:          string Password { get;set;}
   8:          void RedirectFromLoginPage();
   9:          System.Web.UI.WebControls.Button BtnLogin { get;set;}
  10:      }
  11:  }

  • Edit the default aspx page and enter: Welcome you are authenticated!
  • Add the login.aspx to the ZooWebsite project.
  • Edit the source of the login.aspx part -> add two textboxes, a button, and validators:
<%@ Page Language="C#" AutoEventWireup="true" Codebehind="Login.aspx.cs" Inherits="ZooApplication.Website.Login" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Login page</title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
                Login form<br />
                <asp:Label ID="LblErrorMsg" runat="server" Text="Invalid login" Visible="false" ></asp:Label><br />
                Email Address:
                <asp:TextBox ID="TxbEmailAddress" runat="server"></asp:TextBox>
                <asp:RequiredFieldValidator ID="RfvEmailAddress" runat="server" ErrorMessage="Enter your email address!"
                    ControlToValidate="TxbEmailAddress">
                </asp:RequiredFieldValidator>
                <asp:RegularExpressionValidator ID="RevEmailAddress" runat="server" ControlToValidate="TxbEmailAddress"
                    ErrorMessage="Invalid email address!" ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*">
                </asp:RegularExpressionValidator></div>
            <div>
                Password:
                <asp:TextBox ID="TxbPassword" runat="server"></asp:TextBox>
                <asp:RequiredFieldValidator ID="RfvPassword" runat="server" ErrorMessage="Enter your password!"
                    ControlToValidate="TxbPassword">
                </asp:RequiredFieldValidator>
            </div>
            <div>
                <asp:Button ID="PageBtnLogin" runat="server" Text="Login" />
            </div>
        </div>
    </form>
</body>
</html>

Because the code-behind is not testable and don’t make part of the SUT (Subject Under Test) we want to diminish the code-behind logic to a bare minimum.   The view responsibility is limited to output  the data coming from our model in a human readable way and to expose user input to the controller.

Therefore we implement our interface through an aspx page that only contains a set of properties that binds data coming from the controller with our web controls. 

Generally we will try to implement all the presentation logic into the controller. The only exception here will be the RedirectFromLoginPage() method.

   1:  using System;
   2:  using System.Data;
   3:  using System.Configuration;
   4:  using System.Collections;
   5:  using System.Web;
   6:  using System.Web.Security;
   7:  using System.Web.UI;
   8:  using System.Web.UI.WebControls;
   9:  using System.Web.UI.WebControls.WebParts;
  10:  using System.Web.UI.HtmlControls;
  11:  using ZooApplication.Library;
  12:   
  13:  namespace ZooApplication.Website
  14:  {
  15:      public partial class Login : System.Web.UI.Page, ZooApplication.Library.ILoginView
  16:      {
  17:   
  18:          public string EmailAddress
  19:          {
  20:              get
  21:              {
  22:                  return TxbEmailAddress.Text;
  23:              }
  24:              set
  25:              {
  26:                  TxbEmailAddress.Text = value;
  27:              }
  28:          }
  29:   
  30:          public string ErrorMessage
  31:          {
  32:              get
  33:              {
  34:                  return LblErrorMsg.Text;
  35:              }
  36:              set
  37:              {
  38:                  LblErrorMsg.Text = value;
  39:              }
  40:          }
  41:   
  42:          public string Password
  43:          {
  44:              get
  45:              {
  46:                  return TxbPassword.Text;
  47:              }
  48:              set
  49:              {
  50:                  TxbPassword.Text = value;
  51:              }
  52:          }
  53:   
  54:          public Button BtnLogin
  55:          {
  56:              get
  57:              {
  58:                  return PageBtnLogin;
  59:              }
  60:              set
  61:              {
  62:                  PageBtnLogin = value;
  63:              }
  64:          }
  65:   
  66:          public void RedirectFromLoginPage()
  67:          {
  68:              FormsAuthentication.RedirectFromLoginPage(this.EmailAddress, false);
  69:          }
  70:      }
  71:  }

 

The model

It’s our model that is responsible to validate the user login and password against the DB. 

We create a DB named ZooDB:

  • Add an APP_Data folder to your ZooWebsite project
  • APP_Data, new item, Database

Execute this script to create a table Profiles on the ZooDB:

CREATE TABLE [dbo].[Profiles](
      [ProfileID] [int] IDENTITY(1,1) NOT NULL,
      [EmailAddress] [nvarchar](255) NOT NULL,
      [Password] [nvarchar](255) NOT NULL,
 CONSTRAINT [PK_Profiles] PRIMARY KEY CLUSTERED
(
      [ProfileID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

Now configure your web.config file to add the connectionstring and the authentication part:

<?xml version="1.0"?>
<configuration>
  <connectionStrings>
    <add name="ZooDB" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=.\App_Data\ZooDB.mdf;Integrated Security=True"/>
  </connectionStrings>
    <system.web>
      <authentication mode="Forms">
        <forms name="AuthCookie" path="/" loginUrl="login.aspx" protection="All" timeout="10">
        </forms>
      </authentication>
      <authorization>
        <deny users="?"/>
      </authorization>
    </system.web>
</configuration>

image002

Test the application, it should compile and we should be redirected to the login page

image004

Our model implements an Authenticate method. Again we will make use of interfaces to decouple the model from the controller.

On the ZooLibrary project create an interface ILoginModel:

   1:  namespace ZooApplication.Library
   2:  {
   3:      public interface ILoginModel
   4:      {
   5:          bool Authenticate(string emailAddress, string password);
   6:      }
   7:  }

 

The method Authenticate of the class LoginModel will check the validity of supplied email address and password.  You can implement the model with your preferred data access code.  Personally I use Subsonic because it’s really simple to use and it’s based on the active record pattern, the same pattern used in Rails.

But for the moment let’s use standard ADO.NET code:

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Text;
   4:  using System.Data.SqlClient;
   5:  using System.Configuration;
   6:   
   7:  namespace ZooApplication.Library
   8:  {
   9:      public class LoginModel : ILoginModel
  10:      {
  11:          public bool Authenticate(string emailAddress, string password)
  12:          {
  13:              SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["ZooDB"].ConnectionString);
  14:              SqlCommand cmd = new SqlCommand(
  15:                  "SELECT count(EmailAddress) FROM [Profiles] " +
  16:                  "WHERE EmailAddress=@EmailAddress AND Password=@Password",
  17:                  conn);
  18:              cmd.Parameters.AddWithValue("@EmailAddress", emailAddress);
  19:              cmd.Parameters.AddWithValue("@Password", password);
  20:              try
  21:              {
  22:                  conn.Open();
  23:                  if((int)cmd.ExecuteScalar()==1)
  24:                      return true;
  25:   
  26:                  return false;
  27:              }
  28:              finally
  29:              {
  30:                  conn.Close();
  31:              }
  32:          }
  33:      }
  34:  } 

The controller

The controller job is to figure out how the view should display the model. Therefore the controller should have an association with the model and the view.

  • Add a new class to ZooLibrary name it LoginController.

We start by defining a constructor that takes a ILoginView and a ILoginModel as parameters.
The Initialize method will be called by the page to instruct the controller to take control over the view and the model.
In the initialize method we will prepare the view to be rendered and subscribe to the events triggered by the view.

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Text;
   4:   
   5:  namespace ZooApplication.Library
   6:  {
   7:      public class LoginController
   8:      {
   9:          private ILoginView _view;
  10:          private ILoginModel _model;
  11:         
  12:   
  13:          public LoginControler(ILoginView view, ILoginModel model)
  14:          {
  15:              this._view = view;
  16:              this._model = model;         
  17:          }
  18:   
  19:          public void Initialize()
  20:          {
  21:              this._view.ErrorMessage = "";
  22:              this._view.BtnLogin.Click += new EventHandler(BtnLogin_Click);
  23:          }
  24:   
  25:          public void BtnLogin_Click(object sender, EventArgs e)
  26:          {
  27:              if (this._model.Authenticate(this._view.EmailAddress, this._view.Password))
  28:                  this._view.RedirectFromLoginPage();
  29:              else
  30:                  this._view.ErrorMessage = "Invalid emailaddress or password!";
  31:          }
  32:      }
  33:  }

clip_image002

 

Integrating the MVC into the page

When we program against an asmx page it’s always the page that receive the initial control from the ASP.NET framework.
So it’s the page that need to instantiate the model, the view and the controller.

   1:      public partial class Login : System.Web.UI.Page, ZooApplication.Library.ILoginView
   2:      {
   3:          private LoginController controller;
   4:   
   5:          protected override void OnInit(EventArgs e)
   6:          {
   7:              base.OnInit(e);
   8:   
   9:              ILoginModel model = new LoginModel();
  10:              controller = new LoginController(this, model);
  11:          }
  12:          protected void Page_Load(object sender, EventArgs e)
  13:          {
  14:              controller.Initialize();
  15:          }
  16:  
  17:       }
 

Testing

We are now able with the help of mocking frameworks to test the logic in the model and the controller.

The code here uses NMock but you can use your prefered mocking framework to implement your tests:
In the meantime the test for our LoginController should look like this:

   1:  public void LoginController_LoginTest()
   2:          {
   3:              ILoginView view = mocks.NewMock<ILoginView>();
   4:   
   5:              ILoginModel model = mocks.NewMock<ILoginModel>();
   6:              LoginController target = new LoginController(view, model);
   7:             
   8:              Expect.AtLeastOnce.On(view).GetProperty("BtnLogin").Will(Return.Value(new System.Web.UI.WebControls.Button()));
   9:              Expect.AtLeastOnce.On(view).GetProperty("EmailAddress").Will(Return.Value("unitEmail@test.be"));
  10:              Expect.AtLeastOnce.On(view).GetProperty("Password").Will(Return.Value("password"));
  11:              Expect.Once.On(model).Method("Authenticate").With(view.EmailAddress, view.Password).Will(Return.Value(true));
  12:              Expect.Once.On(view).SetProperty("ErrorMessage").To("");
  13:              Expect.AtLeastOnce.On(view).Method("RedirectFromLoginPage");
  14:   
  15:              target.Initialize();
  16:              target.BtnLogin_Click(view.BtnLogin, null);
  17:   
  18:              mocks.VerifyAllExpectationsHaveBeenMet();
  19:             
  20:          }

I hope this introduction to the MVC pattern has been profitable to you.

 

kick it on DotNetKicks.com

Extensibility for ASP.NET MVC

 

I’m working on an extensibility framework made to be used on top of ASP.NET MVC. My extensibility framework is based on the famous Ioc container: Structuremap .
The use case I’m trying to fulfill is simple: create an application that should have some basic functionality that can be extended for every customer (=multi-tenancy). There should only be one instance of the application hosted but this instance can be adapted for every customer without making any changes to the core website.
I was inspired by the article on multi tenacy wroted by Ayende Rahien: http://ayende.com/Blog/archive/2008/08/16/Multi-Tenancy–Approaches-and-Applicability.aspx Another source of inspiration was the book of Eric Evans on Domain Driven Design. My Extensibility framework is based on the repository pattern and the concept of root aggregates. To be able to use the framework the hosting application should be build around repositories and domain objects. The controllers, repositories or domain objects are bind at runtime by the ExtensionFactory.
A plug-in is simply an asselmbly that contains Controllers or Repositories or Domain Objects that respects a specific naming convention. The naming convention is simple, every class should be prefixed by the customerID e.g.: AdventureworksHomeController.
To extend an application you copy a plug-in assembly in the extension folder of the application. When a user request a page under the customer root folder e.g: http://multitenant-site.com/[customerID]/[controller]/[action] the framework check if there is a plug-in for that particular customer and instantiate the custom plug-in classes otherwise it loads the default once. The custom classes can be Controllers – Repositories or Domain Objects. This approach enables to extend an application at all levels, from the database to the UI, through the domain model, repositories.
When you want to extend some existing features you create a plug-in an assembly that contains subclasses of the core application. When you’ve to create totally new functionalities you add new controllers inside the plug-in. These controllers will be loaded by the MVC framework when the corresponding url is requested. If you want to extend the UI you can create a new view inside the extension folder and reference the view by a new or subclassed controller .To modify existing behavior you can create new repositories or domain objects or sub classing exiting ones. The framework responsibility is to determine which controller/ repository / domain object should be loaded for a specific customer.
I advise to have a look at structuremap (
http://structuremap.sourceforge.net/Default.htm) and especially at the Registry DSL features http://structuremap.sourceforge.net/RegistryDSL.htm .
This is the code I use at the startup of the application to register all plug-in controllers/repositories or domain objects:
protectedvoidScanControllersAndRepositoriesFromPath(string path)
{
   
this.Scan(o =>
    {
       o.
AssembliesFromPath(path);
       o.
AddAllTypesOf<SaasController>().NameBy(type => type.Name.Replace(“Controller”, “”));
       o.
AddAllTypesOf<IRepository>().NameBy(type => type.Name.Replace(“Repository”, “”));
       o.
AddAllTypesOf<IDomainFactory>().NameBy(type => type.Name.Replace(“DomainFactory”, “”));
       });
}
I also use an ExtensionFactory inheriting from the System.Web.MVC. DefaultControllerFactory. This factory is responsible to load the extension objects (controllers/registries or domain objects). You can plugin your own factories by registering them at startup in the Global.asax file:
protectedvoid Application_Start()
{
   
ControllerBuilder.Current.SetControllerFactory(
      
newExtensionControllerFactory()
    );
}
l’ll try to find some time to explain my approach more clearly on my blog and hopefully I’ll be able to publish my Extesnsibility framework on CodePlex soon.