Building upon what I started with in my last post – a controller like cshtml page in WebMatrix – I have now added some functionality which means I have a basic, but working (demo state), generic CRUD workflow mimicing important parts of a regular Asp Net MVC 3 application.
The code here is in an experimental state but I have decided to write about it already because both I think it works out well and also I would very much appreciate any feedback I can get.
In this next step after my initial MVC-ish lab in WebMatrix I add data handling with the use of Mark Rendles geniously dynamic SimpleData data access library.
Addressing some of the problems I experience with Asp Net MVC 3 programming
Long story short – I am trying to use good parts from popular dynamic languages MVC frameworks in my .net Web programming. Parts where MVC 3 still is rather clumsy. I dont want to tire you with a theoretic discussion just now – instead I want you follow me through a code explanation. (Look for the short theory at the bottom of this post if you like.)
Here goes:
The controller
Here’s what I do in my controller /person.cshtml for the “edit” action:
// Define the SimpleData db connection and the table for the use
// in this controller.
var DbFile = Server.MapPath("~/App_Data/StarterSite.sdf");
var Db = Simple.Data.Database.OpenFile(DbFile);
Page.SimpleDataTable = Db.Person;
// Act on the url-action ("edit" / "insert" / "display" / "edit")
switch (action)
{
case "edit":
// Find the record in the database and store in Page.Model
Page.Model = Page.SimpleDataTable.FindById(id);
if (!IsPost)
{
// Render the view
@RenderPage(Page.ControllerUrl + "/_edit.cshtml");
}
else
{
// Update available properties in Page Model using
// Request.Form data
DynamicForm.FormHelpers.UpdatePageModel();
// Try to store data back to the database using
// SimpleData.Update
// If update fails we return to the edit view with the
// error message from SimpleData.
// The real user friendly validation should already be
// taken care of with jQuery validate,
// this is the fallback safety validation.
try
{
Page.SimpleDataTable.Update(Page.Model);
Response.Redirect(Page.ControllerUrl);
}
catch (Exception ex)
{
Page.Message = ex.Message;
@RenderPage(Page.ControllerUrl + "/_edit.cshtml");
}
}
break;
The edit view
/person/_edit.cshtml is our edit view, and works just fine with just a minimal amount of code:
<body>
<h2>Edit record</h2>
@if (Page.Message != "") {
<strong>@Page.Message</strong>
}
@DynamicForm.EditorForModel()
<a href="@Page.ControllerUrl">Back to list</a>
</body>
The helpers
I created a few helper methods in App_Code/DynamicForm.cshtml to make this possible:
@helper EditorForModel()
{...}
@helper InsertForModel()
{...}
@helper DisplayForModel()
{...}
@helper DeleteForModel()
{...}
@helper TableForModel()
{...}
The helpers make use of the data in Page.Model which is handled in the controller, either a single record (for Display, Edit and Delete) or a list of records (TableForModel). I also needed a way to add Label texts, and define the editors. For that I find a dictionary with necessary metadata much easier to work with than a conventional ViewModel with data attributes.
A ViewModelDefinition
To define exactly what I like to show in the view forms I make use of a ViewModelDefinition class with which I define data fields, label texts and CssClasses in the controller, like this:
var def = new DynamicForm.ModelDefinition();
def.AddColumn("id", "hidden");
def.AddColumn("name", "Name", "text", "mandatory");
def.AddColumn("comment", "Comment", "textarea", "mandatory");
def.AddColumn("submit","Submit","submit");
Page.ViewModelDefinition = def;
The (preliminary) EditorForModel
In the EditorFormModel helper I use both Page.Model as well as Page.ViewModelDefinition to make the edit form.
@helper EditorForModel()
{
var modelDict = (IDictionary<string, object>)Page.Model;
<form method="post">
@foreach (var columnDefinition in Page.ViewModelDefinition.Columns)
{
var col = columnDefinition.Value;
string value = "";
if (Page.Model == null)
{
if (col.DefaultValue != null)
{
value = col.DefaultValue.ToString();
}
}
else
if (col.DataField != null && col.DataField != "" && modelDict.Keys.Contains(col.DataField))
{
value = (modelDict[col.DataField] ?? "").ToString();
}
switch ((string)col.Type)
{
case "text":
<div>
<label for="@col.DataField">@col.LabelText</label>
<input type="text" id="@col.DataField" name="@col.DataField" class="@col.CssClass" value="@value"/>
</div>
break;
case "textarea":
<div>
<label for="@col.DataField">@col.LabelText</label>
<textarea id="@col.DataField" name="@col.DataField" class="@col.CssClass">@value</textarea>
</div>
break;
case "hidden":
<div>
<input type="hidden" id="@col.DataField" name="@col.DataField" value="@value"/>
</div>
break;
case "submit":
<div>
<input type="submit" value="@col.LabelText"/>
</div>
break;
}
}
</form>
}
Conclusion
I am still some steps from having code in such a quality where I can distribute this as a open source project, but I hope I will soon, or that someone else is doing it. It’s really no much work in total here as most is already in WebMatrix, and of course SimpleData.
As I said in the beginning of this post I would love to get any feedback on this.
Appendix
The MVC3-problem areas wanted to try to move away from
Data classes and Datacontext complexity. A genuine stable ORM & data layer is something one never should leave behind, but for many web applications I think SimpleData makes db-crud both good enough and as lightweight and “ceremony less” as it can be.
Handle localized (translated) texts. This is easier and more direct with a ViewModelDefinition dictionary than using DataAttributes on ViewModels.
Recompiling controllers. I have come to like the dynamic Rails/Django/Php approach to web development for the closeness and speed between editing code and seing the result. In short – without needing to recompile the full application (and resetting the sessions). By editing cshtml’s the individual files are the only ones that need recompilation and that is done in the blink of the eye.
Scaffolding views. The T4-scaffolding of views is nice and makes often 90% of the work. But the way I (and I guess many with me) work I redesign everything from data to html often in small iterations. And that for that to work good in MVC 3 I would need to edit T4 templates and regenerate views for each iteration. I like the MVC3 templating (EditorFor) better, but not as much as doing everything in a Razor helper where I have great control with the best syntax available imho.
Razor helpers reduce the use of “magic”. Make the helpers your own, change the code therein just like you want and make individual versions for different controllers – when you need.
I polish the helpers easily and the polish shines through to every part of the program where the global helpers are used. (If I need individual behaviour in a view that is easy to do just by copying the global helper and editing it in the particular view).
Tests
I do need testing in my WebMatrix projects. That is something I have to explore further. Any good ideas to share?
Follow up on the dynamicforms
I made a new approach to the dynamic forms in my HtmlFormHelper.
January 29, 2011 
Wow, I was just working on the same type of Mvc-Light approach to WebMatrix/WebPages. I’ve even done some work on making/faking restful-style routes using IIS UrlRewrite Module so that you can get ruby-style restful URLs like /categories/1/posts/1/comments.
I’m planning on maybe putting this into a simple NuGet package as a SimpleMvc framework for use with WebMatrix/WebPages.
I plan on doing a post on it soon and maybe we could collaborate on the effort.
Hi Adam, very glad to hear that! Yeah I’m defo in for some collab. I’ll send you an email. Looking forward to read your post,
Regards, Jonas
Hey Jonas, very impressed with this post.
Question, where will this code set will go?
var def = new DynamicForm.ModelDefinition();
.
.
And also in person.cshtml from where this code comes from:
DynamicForm.FormHelpers.UpdatePageModel();
Am I missing anything.
Thanks.
Hi!
I took another approach to this, here’s the beginning https://gist.github.com/907766, I hope I’ll get it better and more complete soon.
Another good post. Agree with all the MVC pain points mentioned. Have been bouncing back and fourth between the two technologies. Seems like its easier to develop the project in WebMatrix then port it to MVC later……
Good post! We are linking to this particularly
great content on our website. Keep up the great writing.