WebMatrix MVC-ishness with Simple.Data and FormHelpers for CRUD

Standard

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.

About these ads

6 thoughts on “WebMatrix MVC-ishness with Simple.Data and FormHelpers for CRUD

  1. 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.

  2. hrjk

    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.

  3. carelive

    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……

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s