(optional
Download CreateDatabase.zip)
Unzip code of the article into a new Virtual Directory test_mvc (http://localhost/test_mvc/) on your IIS server. Make sure you have enabled ASP scripts for the virtual directory. Make sure the default.asp is a default web page for this Virtual Directory.
Introduction
How many companies are still extensively using ASP this days and why? Typically the case is a company with huge ASP system in the core and few developers who know the system from it’s childhood. The system is usually too complex, with spaghetti-like style of code inside (because of a nature of ASP). I believe there is a little or no ways for new developers to be involved in support or development of the code. The reason is that the threshold for new developers is too high:
- market encourages developers to learn new stuff and forget old ones.
- huge spaghetti-like code makes the effort to study the code meaningless.
These ASP system exist because they work. Existing developers may want to change something, but the the threshold for them is high too:
- high workload with existing app support
- necessity to study new languages (like vb.net or c#) and technologies (like MVC)
What is easier for ASP developer: to write a new code with C#+MVC or continue with ASP?
Microsoft has dropped the support of ASP and there is a little of innovations this days. Some use JavaScript, some use JQuery, XML but that is it. There is very little of innovations for the server-side that may makes the life of programmers easier.
The aim of this effort is to bring MVC concept to ASP. To give a way to write/re-write the ASP app in MVC-like style. Untangle spaghetti-like code. To let newcomers recognize the common code-patterns and pick up the support of existing ASP apps easier.
Background
There
is a MVC out there. Let’s embed MVC patterns into the
classic ASP.
Router
In
the core of MVC there is a Router. Router selects the Controller and
Action (mostly).
Router is a code that decides which Controller and Action to call.
Samples
of URLs (controllers are highlighted)
Http://localhost/Home/Index
Http://localhost/Home/About
Http://localhost/User/Edit/1
Controller
Wth
is this Controller? Why should you care to have Controllers in your
code? I may say that it is a significant part of the MVC concept, but
these words mean nothing.
Let’s
go from practice. It’s rare, but suppose you have a document to
develop a part of some system:

Where
you are going to store the code for this logic? As an ASP developer
you would probably create a set of pages to pull and display some
data from the database, and a couple of pages to receive Forms with
user input to update the database back. It is OK when the system is
not too large. It is OK when the User interacts with the system in a
simple way.
How
is about that system?

You
will have to create some sort of "Architecture" in your code
to do not get totally lost in the code when the system is finished.
When I say "Architecture" I mean some repetitive
patterns of the code to handle the similar situations. Anyways the
system will be a mess of HTML, SQL, code to access the database, handle user
input, and display data at the end of development. By the way, I
believe that the system on the picture is VERY SIMPLE. What
happened if it is 10-20 times bigger?
A
remedy for the problem is the code encapsulation with repetitive
standard structures of the code and special agreement regarding the
naming of the components. So there is a place for special component - a Controller, which stores the business logic and interacts with user:

Controller is a class. It holds all the aspects of user interaction regarding
ONE type of business entity. There should be a number of controllers
in the Application. Controllers MUST have names ending with
Controller: HomeController, PublicationController, StatusController,
UserController. Controller SHOULD NOT have a user interface code or
HTML markup. It is a good practice to avoid the SQL in the controller too. Controller should handle user input, query and update
the object model and prepare the data to be displayed. All Controllers should reside in the folder /Controllers
Controller handles the user input , do the things and controls WHAT is going to be displayed
(but not HOW).
Controller
should have methods
that
compose the logic of Controller.
like this:
Class PublicationController
Dim Model
Dim ViewData
Public Sub List ()
...
End Sub
Publis Sub Edit (vars)
...
End Sub
Publis Sub Delete(vars)
...
End Sub
...
End Class
These Methods of Controller are Actions. Router should be able to call them.
These are samples of URLs with Actions are highlighted.
Http://localhost/Home/Index
Http://localhost/Home/About
Http://localhost/User/Edit/1
Router disassembles the URL and calls one of the Controllers and one of the Actions within the Controller.
There
is no standard Router for ASP. It will be a default.asp to work as a
Router.
The
URLs we are going to employ will be similar to:
http://localhost/test_mvc/default.aspx?controller=Home&action=Index
http://localhost/test_mvc/default.aspx?controller=Home&action=About
http://localhost/test_mvc/default.aspx?controller=User&action=Edit&id=2
or their shorter form (if you have selected default.asp as a default page for the virtual directory):
http://localhost/test_mvc/?controller=User&action=List
There
is no REFLECTION in the VBScript and we don’t know whether there is
a Controller class and it’s Action method out there. And we
don’t need to.
Router
simply calls the Action method of the Controller object with VBScript
Eval
instructions.
So the these two are equal:
Set controllerInstance = New HomeController
controller = "Home"
Set controllerInstance = Eval("New "+ controller + "Controller") But the second one is more flexible and can be used to route the Http request based on the controller name. The Action is selected using the same technique.
The code of Router:
{text of defaut.asp}
<!---->
<!---->
<!---->
<%
Const defaultController = "Home"
Const defaultAction = "Index"
If not Route () then
result = RouteDebug ()
End If
Function ContentPlaceHolder()
If not Route () then
result = RouteDebug ()
End If
End Function
Function Route ()
Dim controller, action , vars
controller = Request.QueryString("controller")
action = Request.QueryString("action")
set vars = CollectVariables()
Route = False
If IsEmpty(controller) or IsNull(controller) then
controller = defaultController
End If
If IsEmpty(action) or IsNull(action) then
action = defaultAction
End If
Dim controllerName
controllerName = controller + "Controller"
Dim controllerInstance
Set controllerInstance = Eval ( " new " + controllerName)
Dim actionCallString
If (Instr(1,action,"Post",1)>0) then
actionCallString = " controllerInstance." + action + "(Request.Form)"
ElseIf Not (IsNothing(vars)) then
actionCallString = " controllerInstance." + action + "(vars)"
Else
actionCallString = " controllerInstance." + action + "()"
End If
Eval (actionCallString)
Route = true
End Function
Function RouteDebug ()
Dim controller, action , vars
controller = Request.QueryString("controller")
action = Request.QueryString("action")
Response.Write(controller)
Response.Write(action)
dim key, keyValue
for each key in Request.Querystring
keyValue = Request.Querystring(key)
if InStr(1,"controller, action, partial",key,1)=0 Then
Response.Write( key + " = " + keyValue )
End If
next
End Function
Function CollectVariables
dim key, keyValue
Set results = Server.CreateObject("Scripting.Dictionary")
for each key in Request.Querystring
keyValue = Request.Querystring(key)
if InStr(1,"controller, action, partial",key,1)=0 Then
results.Add key,keyValue
End If
next
if results.Count=0 Then
Set CollectVariables = Nothing
else
Set CollectVariables = results
End If
End Function
%>
Actions
So the Controller is instantiated and one of it's Actions is called. Next, Action executes the business logic. There may be a business logic of any kind in the controller. You may query or update the database, send e-mails, process files or user input, or even launch a rocket to the Mars. Typically there is a logic to update/insert/delete/list records from/to database, but it may be literally anything there. There should be NO HTML markup in the Controller.
At the end of Action, if you want to display SOMETHING (besides the static content of View), you need to initialize the variable Model and include the View as the last line of the Action.
Look at the example below: If the user hit the link http://localhost/test_mvc/?controller=User&action=List , Router selects the UserController and List action. The List action prepares the list of Users and assign this list to the variable Model. Variable Model is used in the View ../views/User/List.asp This view is attached at the end of action List.
{ text of /Controller/UserController.asp }
<%
class UserController
Dim Model
Dim ViewData
private sub Class_Initialize()
Set ViewData = Server.CreateObject("Scripting.Dictionary")
end sub
private sub Class_Terminate()
end sub
public Sub List()
Dim u
set u = new UserHelper
set Model = u.SelectAll
%> --> <%
End Sub
public Sub Create()
set Model = new User
%> --> <%
End Sub
public Sub CreatePost(args)
Dim obj, objh
set objh = new UserHelper
set obj = new User
obj.FirstName = args("FirstName")
obj.LastName = args("LastName")
obj.UserName = args("UserName")
obj.ProjectID = args("ProjectID")
obj.Id = objh.Insert(obj)
Response.Redirect("?controller=User&action=list")
End Sub
public Sub Edit(vars)
Dim u
set u = new UserHelper
set Model = u.SelectById(vars("id"))
%> --> <%
End Sub
public Sub EditPost(args)
Dim obj, objh
set objh = new UserHelper
set obj = objh.SelectById(args("id"))
obj.FirstName = args("FirstName")
obj.LastName = args("LastName")
obj.UserName = args("UserName")
obj.ProjectID = args("ProjectID")
objh.Update(obj)
Response.Redirect("?controller=User&action=list")
End Sub
public Sub Delete(vars)
Dim u
set u = new UserHelper
set Model = u.SelectById(vars("id"))
%> --> <%
End Sub
public Sub DeletePost(args)
Dim res, objh
set objh = new UserHelper
res = objh.Delete(args("id"))
if res then
Response.Redirect("?controller=User&action=list")
else
Response.Redirect("?controller=User&action=Delete&id=" + CStr(args("id")))
end if
End Sub
public Sub Details(vars)
Dim u
set u = new UserHelper
set Model = u.SelectById(vars("id"))
%> --> <%
End Sub
End Class
%>
Note: View is selected automatically with .NET MVC. View should have the same name as Action. They reside in the folder with the same name as the Controller.For example
/Views/Home/Index.asp
/Views/User/Edit.asp
Views
View provides appearance. It is only Views should have an HTML markup inside them. There should be NO or absolute minimum of the logic in a View. The business of a View is to take the Model and display it to the user in a best way.
{ text of /Views/User/List.aspx }
List Users
<%=Html.ActionLink("Create new User", "User", "Create" , "") %> <br/>
<table>
<tr>
<td>FirstName</td>
<td>LastName</td>
<td>UserName</td>
<td>ProjectID</td>
<td></td>
</tr>
<%
if IsNothing(Model) then
%> <tr><td colspan="4">No records</td> </tr><%
Else
Dim obj
For each obj in Model.Items
%>
<tr>
<td><%=Html.Encode(obj.FirstName) %></td>
<td><%=Html.Encode(obj.LastName) %></td>
<td><%=Html.Encode(obj.UserName) %></td>
<td><%=Html.Encode(obj.ProjectID) %></td>
<td>
<%=Html.ActionLink("Edit", "User", "Edit" , "id=" + CStr(obj.Id)) %> |
<%=Html.ActionLink("Delete", "User", "Delete" , "id=" + CStr(obj.Id)) %> |
<%=Html.ActionLink("Details", "User", "Details" , "id=" + CStr(obj.Id)) %>
</td>
</tr>
<%
Next
End If
%>
</table>
So why these Views are so amazing?
It is all in the Order and Separation.
Design your Views separately from your code. Express Yourself. Change the appearance as easy as 1-2-3. Have a fullscreen or mobile view (or both) without necessity to touch the business logic code.
Concentrate on the business logic and data when you work with Controller/Action.
Scale your system up to dozens Controllers and hundreds of Views. Enjoy the transparency and easy code navigation. Get rid of spaghetti-like code forever.
History
There are going to be the following topics:
Data
flow: Where the Data is ? What the Model is and how to send Model and
ViewData from Action to View?
Common
view for all the pages - masterpages and partial views
Form
handling: User input handling
Server
side master-details pages
Client
side master-details pages