Moving blogs

2 02 2010

Hey everybody,

It looks like it’s official: I’m FINALLY moving my blog over to my company’s blog: http://www.proace.com/blog/

I’ll leave this one up for old time sake, but I’d like to move more towards a professional and very technical blog so the days of “creative writing” are definitely gone (for now at least).

Technorati Tags:

Comments No Comments »

FubuToDo – Part 2: Forms, Controllers, View, and jQuery

17 01 2010

This is the second post in a series of posts I am writing on creating a simple “To-Do” application using FubuMVC. I’ve broken down this series into the following:

  1. Part 1: Conventions, Opinions and Bootstrapping
  2. Part 2: Forms, Controllers, Views, and jQuery
  3. Part 3: Persistence

Overview

I wanted to create a simple “To Do” application using FubuMVC. In my previous post, I showed the basics for getting your controllers created, tested, and wired up. I also explained how to setup your conventions and how to bootstrap your application. This time, I’m going to talk about creating forms, actions on controllers to respond to those forms, and how to work in some jQuery.

In order to have a functioning “To-Do” application, we’re missing the following:

  1. Add/Edit/Remove Functionality
  2. Validation
  3. Persistence

We’re going to talk about #1 in this part.

Controllers

As always, let’s start with some tests. I like to group entity-related actions into more semantic urls (e.g., “Items/Add”, “Items/Edit”). I’m just going to group those actions in a single controller so I came up with the following tests for my ItemsController:

  1. When adding an item, the controller calls Insert on the IUnitOfWork
  2. When adding an item, the appropriate ViewModel is returned
  3. When editing an item, the controller calls Update on the IUnitOfWork
  4. When editing an item, the appropriate ViewModel is returned
  5. When removing an item, the controller calls Delete on the IUnitOfWork
  6. When removing an item, the appropriate ViewModel is returned

Now, I know that I want to implement the add/edit/remove functionality via a modal dialogue. I’ve found I can write reusable javascript handlers for responses like this when the result is all the same for CRUD-based operations. To make it easier, I’ve decided to make the Add/Edit/Remove methods return an instance of the JsonUnitOfWorkResult class:

JsonUnitOfWorkResult

/// <summary>
/// Represents the result of a unit of work.
/// </summary>
public class JsonUnitOfWorkResult
{
    /// <summary>
    /// Gets or sets a message describing the result.
    /// </summary>
    public string Message { get; set; }
    /// <summary>
    /// Gets or sets a flag indicating whether the unit of work was successful.
    /// </summary>
    public bool Success { get; set; }
}

And now a sample of our tests:

Test 1

/// <summary>
/// When adding an item, the controller calls Insert on the IUnitOfWork.
/// </summary>
[Test]
public void Add_Item_Calls_Insert()
{
    IUnitOfWork unitOfWork = _mockRepository.StrictMock<IUnitOfWork>();
    ItemsController controller = new ItemsController(unitOfWork,
        _mockRepository.DynamicMock<IToDoItemRepository>());

    using(_mockRepository.Ordered())
    {
        unitOfWork.Expect(uow => uow.Insert(null)).IgnoreArguments();
        unitOfWork.Expect(uow => uow.Commit());
    }

    _mockRepository.ReplayAll();

    AddItemInputModel inputModel = new AddItemInputModel
    {
        Description = "Hello, World!"
    };

    controller.Add(inputModel);

    _mockRepository.VerifyAll();
}

Test 2

/// <summary>
/// When removing an item, the appropriate ViewModel is returned.
/// </summary>
[Test]
public void Remove_Item_Sets_Result()
{
    IUnitOfWork unitOfWork = _mockRepository.StrictMock<IUnitOfWork>();
    ItemsController controller = new ItemsController(unitOfWork,
        _mockRepository.DynamicMock<IToDoItemRepository>());

    RemoveItemInputModel inputModel = new RemoveItemInputModel
    {
        ItemId = 1
    };

    JsonUnitOfWorkResult result = controller.Remove(inputModel);
    Assert.IsNotNull(result);
}

As I’ve mentioned before, Fubu has the notion of “one model in, one model out” – following this gives you a lot of control over your behaviors which I’ll explain later in this post. To make things a little easier, I created an abstract class for all of my CRUD action input models:

ItemInputModel

/// <summary>
/// Provides a base class for all <see cref="ToDoItem"/> input models.
/// </summary>
public abstract class ItemInputModel
{
    /// <summary>
    /// Gets or sets the unique identifier of the item.
    /// </summary>
    public int ItemId { get; set; }
    /// <summary>
    /// Gets or sets the description of the item.
    /// </summary>
    public string Description { get; set; }
}

The actual controller implementation is incredibly easy (admittedly I could’ve done it better). Here’s the Add method:

/// <summary>
/// Adds a new item.
/// </summary>
/// <param name="itemInputModel">The item to add.</param>
/// <returns></returns>
public JsonUnitOfWorkResult Add(AddItemInputModel itemInputModel)
{
    try
    {
        _unitOfWork.Insert(new ToDoItem
                               {
                                   Description = itemInputModel.Description
                               });

        _unitOfWork.Commit();

        return new JsonUnitOfWorkResult
                   {
                       Success = true,
                       Message = "Item added successfully"
                   };
    }
    catch (Exception exc)
    {
        return new JsonUnitOfWorkResult
                   {
                       Success = false,
                       Message = exc.Message
                   };
    }
}

Forms

Ok, we’ve got a working controller. Now let’s setup our forms. I’m going to use jQuery and jQuery UI to handle my dialogs. Let’s take a look at what I wanted the application to do:

  1. Display a list of to-do items
  2. Allow me to add a new item
  3. Allow me to specify that an item is completed or not
  4. Allow me to edit an existing item
  5. Allow me to remove an existing item

We can already do the first requirement. However, since I’m planning on doing the rest through modals, we know we’re going to have to respond to some commands and refresh that list. Let’s revisit the Home/Index.aspx view and refactor our the displaying of those items:

Step 1: Move to a partial view

ItemListViewModel

/// <summary>
/// Provides a model containing a list of <see cref="ToDoItem"/> entities.
/// </summary>
public class ItemListViewModel
{
    /// <summary>
    /// Gets or sets the associated collection of items.
    /// </summary>
    public IEnumerable<ToDoItem> Items { get; set; }
}

List.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="List.aspx.cs" Inherits="FubuToDo.Web.Controllers.Item.List" %>
<%@ Import Namespace="System.Linq"%>
<% if(Model.Items.Any()) { %>
<ul class="item-list">
<% foreach (var item in Model.Items) { %>
    <li id="<%= item.ItemId %>" class="item">
        <div class="row no-label">
            <input type="checkbox" name="Item-<%= item.ItemId %>" <%= item.IsComplete ? "checked=\"checked\"" : string.Empty %> />
            <label class="checkbox"><a href="javascript:return false;"><%= item.Description %></a></label>
        </div>
        <br class="cboth" />
    </li>
<% } %>
</ul>
<% } else { %>
<span>No items.</span>
<% } %>

Step 2: Defer list rendering to an ajax call

In order to do this, I removed the Items property off of the IndexViewModel and removed the list rendering from the Home/Index.aspx view. Let’s take a look at the following snippets:

Index.aspx

<div id="blog-entry-container">
    <div class="success" style="display:none;"></div>
    <div class="error" style="display:none;"></div>
    <h2>To-Do List</h2>
    <div id="Item-List">
    </div>
    <button class="button-prim-med" type="button" value="Add" id="Add-New">
        <span>Add New</span>
    </button>
</div>
<div id="ItemDialog" style="display: none;">
    <form id="frmItem" method="post">
        <input type="hidden" id="hdnItemId" name="ItemId" />
        <fieldset>
            <div class="row">
                <label>
                    Description:</label>
                <input type="text" id="Item-Description" name="Description" class="text required" />
            </div>
        </fieldset>
    </form>
</div>

Now the Home/Index.aspx also references the Scripts/home/index.js file which will handle loading this information. Let’s take a look:

home.js

function reloadItems() {
    var $list = $('#Item-List');
    $list.html('<span>Loading...</span>');

    $.ajax({
        url: '/Items/List',
        type: 'GET',
        contentType: "application/json; charset=utf-8",
        data: { random: new Date().getUTCMilliseconds() },
        success: function(data) {
            $list.html(data);
            bindEditLinks();            bindItemCheckboxes();
        },
        timeout: 5000,
        error: function(xmlObj, textStatus, errorThrown) {
            $list.html('<span>Error</span>');
        }
    });
};

Our Items/List.aspx view returns a simple html snippet. Since we haven’t told Fubu any differently, it treats it as HTML and the appropriate content type is set. Our simple GET request will receive this content and set the contents of that empty div we created in our Home/Index.aspx view. If you take a look at the file a little deeper, you’ll see I call reloadItems on document ready.

You might have noticed the additional “random” parameter that we’re sending in. This this is a GET request, most browsers will cache the result. I threw this in there to avoid bang-head-into-desk syndrome.

Ok, now when I run it I get my items rendering through my ajax call. Now to wire up my dialogs and get it all working.

Adding/Editing an Item

I’ve setup the form in a way that could be used to add or edit an item. Since our input models are virtually identical, all we have to do is change the action of the form. We already have our dialog worked out (see Home/Index.aspx) so let’s wire up our buttons and links:

Wiring up Add Item

function addItem() {
    var itemForm = $('#frmItem');
    var itemDialog = $('#ItemDialog');
    itemForm.clearForm();

    // set up buttons
    itemDialog.dialog('option', 'buttons', {
        'Create': function() {
            itemForm.attr('action', '/Items/Add');
            itemForm.submit();
        },
        Cancel: function() {
            itemForm.clearForm();
            $(this).dialog('close');
        }
    });
    itemDialog.dialog('option', 'title', 'Add Item');

    // show the dialog
    itemDialog.dialog('open');
};

Wiring up Edit Item

function bindEditLinks() {
    $('.item-list > .item > div.row > label > a').click(function() {
        var itemForm = $('#frmItem');
        var itemDialog = $('#ItemDialog');

        var listItemId = $(this).parent().parent().parent().attr('id');
        itemForm.find('#hdnItemId').val(listItemId);
        itemForm.find('#Item-Description').val($(this).html());

        // set up buttons
        itemDialog.dialog('option', 'buttons', {
            'Save': function() {
                itemForm.attr('action', '/Items/Edit');
                itemForm.submit();
            },
            'Remove': function() {
                itemForm.attr('action', '/Items/Remove');
                itemForm.submit();
            },
            Cancel: function() {
                itemForm.clearForm();
                $(this).dialog('close');
            }
        });
        itemDialog.dialog('option', 'title', 'Edit Item');

        // show the dialog
        itemDialog.dialog('open');
    });
};

Wiring up Item Complete:

function bindItemCheckboxes() {
    $(".item-list > .item > div.row > input[type='checkbox']").click(function() {
        var listItemId = $(this).parent().parent().attr('id');
        var description = $(this).parent().find('label > a').html();

        $.ajax({
            url: '/Items/Edit',
            type: 'POST',
            dataType: 'json',
            data: {
                IsComplete: document.getElementById($(this).attr('id')).checked,
                ItemId: listItemId,
                Description: description
            },
            success: function(response) {
                jsonTransactionHandler(response);
            },
            error: function(responseObj, textError, errorCode) {
                $('.error').html(textError).fadeIn('slow', function() {
                    setTimeout(function() { $('.error').fadeOut(); }, 4000);
                });
            }
        });
    });
};

Three things here to note here:

  1. bindEditLinks is called in the reloadItems function.
  2. bindItemCheckboxes is also called in the reloadItems function. This simply submits a POST message to the Edit action with the appropriate fields.
  3. The ‘Remove’ button sets the action of the form to ‘Items/Remove’ and submits it which will successfully remove the item.

Let’s take a look at how we have our form being submitted and handled:

Form Initialization

// initialize the form
$('#frmItem').ajaxSubmit({
    dataType: 'json',
    success: function(response) {
        jsonTransactionHandler(response);
        $('#ItemDialog').dialog('close');
        reloadItems();
    },
    error: function(responseObj, textError, errorCode) {
        $('.error').html(textError).fadeIn('slow', function() {
            setTimeout(function() { $('.error').fadeOut(); }, 4000);
        });
    }
});

Result Handler

function jsonTransactionHandler(response) {
    if (response.Success) {
        $('.success').html(response.Message);
        $('.success').fadeIn('slow', function() {
            setTimeout(function() { $('.success').fadeOut(); }, 4000);
        });
    } else {
        $('.error').html(response.Message);
        $('.error').fadeIn('slow', function() {
            setTimeout(function() { $('.error').fadeOut(); }, 4000);
        });
    }
};

Any successful HTTP response from the controller will trigger the jsonTransactionHandler (ff you remember, we set up our JsonUnitOfWorkResult to let us handle all responses the same way). The dialog then gets closed and the items get reloaded to reflect the changes.

Getting it to run

There are only two changes we’ll need to make to get these changes up and running w/ Fubu:

FubuStructureMapBoostrapper (line 32):

ObjectFactory.Initialize(x =>
                             {
                                 x.For<IToDoItemRepository>().Use<FakeToDoItemRepository>();
                                 x.For<IUnitOfWork>().Use<FakeUnitOfWork>();
                             });

We’re registering a fake unit of work for our ItemsController.

FubuToDoRegistry (last line of the constructor):

JsonOutputIf.WhenTheOutputModelIs<JsonUnitOfWorkResult>();

This tells Fubu that we want a JSON response whenever it encounters our JsonUnitOfWorkResult class.

That’s it! We’re all done. In my next part I’ll talk implementing validation and switching over our repository and unit of works classes to enable some real persistence.

Source code

You can download the source for this part at: http://svn.joshua-arnold.com/fubutodo/tags/part2

Technorati Tags: , ,

Comments 4 Comments

FubuToDo – Part 1: Conventions, Opinions, and Bootstrapping

16 01 2010

With the recent activity from the FubuMVC team, I thought that I would help others get started by providing some samples. Tim Tyrrell started us off with his two posts:

  1. “Hello World” with FubuMVC (Super Quick Start)
  2. “Hello World” with FubuMVC (without the training wheels)

Building off of Tim’s directions, I created a simple but more involved application to show some jQuery magic and really just get started with the framework. I’ve broken down this series into the following:

  1. Part 1: Conventions, Opinions and Bootstrapping
  2. Part 2: Forms, Controllers, Views, and jQuery
  3. Part 3: Persistence

Overview

I wanted to create a simple “To Do” application using FubuMVC. I threw together some quick css and figured I’d make something similar to figure 1.1.:

Figure1.1

Figure 1.1 – Wireframe

One important thing to note is that I built this application using a very specific approach: don’t think too much. A lot of the refactoring that you’ll see me do came to mind before I wrote the original pieces (and I’m sure you’ll see it too) but I decided to just stay the course and see what Fubu can do.

Each “Part” in this series has a corresponding tag in the repository I used which I’ll link to at the end of each article.

Getting Started

I started with the tests for my home controller. I wasn’t too sure how I wanted everything to work just yet so I came up with the following cases to test against:

  1. Given some input model (“IndexInputModel”), the Home controller should return a instance of “IndexViewModel” and it should not be null.
  2. When the home controller creates the “IndexViewModel”, it should set the “Items” property.

Let’s take a look at those tests:

Test 1

/// <summary>
/// Given some input model ("IndexInputModel"), the Home controller should return a instance of "IndexViewModel" and it should not be null.
/// </summary>
[Test]
public void Index_Sets_ViewModel()
{
    IToDoItemRepository itemRepository = _mockRepository.DynamicMock<IToDoItemRepository>();
    HomeController controller = new HomeController(itemRepository);

    IndexViewModel viewModel = controller.Index(new IndexInputModel());
    Assert.IsNotNull(viewModel);
}

Test 2

/// <summary>
/// When the home controller creates the "IndexViewModel", it should set the "Items" property.
/// </summary>
public void Index_Sets_ViewModel_Items()
{
    IToDoItemRepository itemRepository = _mockRepository.DynamicMock<IToDoItemRepository>();
    HomeController controller = new HomeController(itemRepository);

    using(_mockRepository.Ordered())
    {
        itemRepository.Expect(r => r.GetAll()).Return(new List<ToDoItem>());
    }

    _mockRepository.ReplayAll();

    IndexViewModel viewModel = controller.Index(new IndexInputModel());
    Assert.IsNotNull(viewModel.Items);
}

Simple, right?

Now, let’s take a look at the other classes:

IndexInputModel

/// <summary>
/// Provides an input model for the home index view.
/// </summary>
public class IndexInputModel
{
}

IndexViewModel

/// <summary>
/// Provides a model for the home index view.
/// </summary>
public class IndexViewModel
{
    /// <summary>
    /// Gets or sets the associated collection of <see cref="ToDoItem"/> entities.
    /// </summary>
    public IEnumerable<ToDoItem> Items { get; set; }
}

ToDoItem

/// <summary>
/// Represents a to-do item.
/// </summary>
public class ToDoItem
{
    /// <summary>
    /// Gets or sets the unique identifier of the item.
    /// </summary>
    public virtual int ItemId { get; set; }
    /// <summary>
    /// Gets or sets a description of the item.
    /// </summary>
    public virtual string Description { get; set; }
    /// <summary>
    /// Gets or sets a flag indicating whether the item has been completed.
    /// </summary>
    public virtual bool IsComplete { get; set; }
}

HomeController

/// <summary>
/// Provides a controlling mechanism for the home area.
/// </summary>
public class HomeController
{
    private readonly IToDoItemRepository _itemRepository;
    /// <summary>
    /// Initializes a new instance of the <see cref="HomeController"/> class with the specified dependency.
    /// </summary>
    /// <param name="itemRepository">The item repository.</param>
    public HomeController(IToDoItemRepository itemRepository)
    {
        _itemRepository = itemRepository;
    }
    /// <summary>
    /// Prepares the index view model.
    /// </summary>
    /// <param name="model">The input model.</param>
    /// <returns></returns>
    public IndexViewModel Index(IndexInputModel model)
    {
        return new IndexViewModel
                   {
                       Items = _itemRepository.GetAll()
                   };
    }
}

Piecing it together

Ok, so now I had a working controller. What I needed to do next was move my classes around, create a view, and then wire everything up. I went with three projects:

  1. FubuToDo
    1. Domain
    2. Repositories
    3. Infrastructure code will go here too
  2. FubuToDo.Tests
  3. FubuToDo.Web
    1. Controllers – Folder for each
    2. Common – Master pages
    3. Images
    4. Styles
    5. Infrastructure – Bootstrapping, registries

I took up some habits from the Dovetail guys and organized my views, models, and controllers in the same folder. Fubu uses the notion of “one model in, one model out” and the controllers are intended to be very light. Given those conventions, it’s pretty safe to assume that your folders won’t blow up with a ton of files and it’s just easier to find everything. I ended up with this:

Figure1.2

Figure 1.2 – Solution structure

The Index view doesn’t have anything fancy. The code-behind class inherits from FubuPage<IndexViewModel> and here’s the code for rendering the items:

    <div id="blog-entry-container">
        <h2>To-Do List</h2>
        <div id="Item-List">
            <% if(Model.Items.Any()) { %>
            <ul class="item-list">
            <% foreach (var item in Model.Items) { %>
                <li id="<%= item.ItemId %>" class="item">
                    <div class="row no-label">
                        <input type="checkbox" name="Item-<%= item.ItemId %>" <%= item.IsComplete ? "checked=\"checked\"" : string.Empty %> />
                        <label class="checkbox"><%= item.Description %></label>
                    </div>
                    <br class="cboth" />
                </li>
            <% } %>
            </ul>
            <% } else { %>
            <span>No items.</span>
            <% } %>
        </div>
    </div>

Getting it to run

We’re almost there, I promise. We’ve got everything working. Now we just need to tell Fubu what to do with it all. Let’s take a look at the Global.asax.cs file:

Global.asax.cs

/// <summary>
/// Provides a global application class for the to-do list application.
/// </summary>
public class Global : HttpApplication
{
    /// <summary>
    /// Called by the ASP.NET Framework on application start.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    protected void Application_Start(object sender, EventArgs e)
    {
        var routes = RouteTable.Routes;
        FubuStructureMapBootstrapper.Bootstrap(routes);
    }
}

We’re deferring all of the initialization and registry calls to our FubuStructureMapBootstrapper class. It’s got a method called “Bootstrap” that just takes in our route collection. Let’s take a look under the hood (if you’ve read Tim’s post, this should look extremely familiar):

FubuStructureMapBoostrapper

/// <summary>
/// Simple StructureMap bootstrapper.
/// </summary>
public class FubuStructureMapBootstrapper : IBootstrapper
{
    private readonly RouteCollection _routes;
    /// <summary>
    /// Initializes a new instance of the <see cref="FubuStructureMapBootstrapper"/> class.
    /// </summary>
    /// <param name="routes"></param>
    public FubuStructureMapBootstrapper(RouteCollection routes)
    {
        _routes = routes;
    }
    /// <summary>
    /// Initializes the bootstrapping process.
    /// </summary>
    public void BootstrapStructureMap()
    {
        UrlContext.Reset();

        ObjectFactory.Initialize(x => x.For<IToDoItemRepository>().Use<FakeToDoItemRepository>());

        Bootstrap(ObjectFactory.Container, _routes);
    }
    /// <summary>
    /// Bootstraps the application with the specified container.
    /// </summary>
    /// <param name="container"></param>
    /// <param name="routes"></param>
    public static void Bootstrap(IContainer container, RouteCollection routes)
    {
        var bootstrapper = new StructureMapBootstrapper(container, new FubuToDoRegistry());
        bootstrapper.Bootstrap(routes);
    }
    /// <summary>
    /// Bootstraps the application for the specified routes.
    /// </summary>
    /// <param name="routes"></param>
    public static void Bootstrap(RouteCollection routes)
    {
        new FubuStructureMapBootstrapper(routes).BootstrapStructureMap();
    }
}

There are two things to note here: 1) The FakeToDoItemRepository and 2) the FubuToDoStructureMapRegistry classes.

The FakeToDoItemRepository class simply returns a hard-coded List<ToDoItem> full of 3 to-do items. The FubuToDoRegistry class has all of my registry calls which tell Fubu what conventions I would like for my app:

FubuToDoRegistry

/// <summary>
/// Simple registry.
/// </summary>
public class FubuToDoRegistry : FubuRegistry
{
    /// <summary>
    /// Initializes a new instance of the <see cref="FubuToDoRegistry"/> class.
    /// </summary>
    public FubuToDoRegistry()
    {
        IncludeDiagnostics(true);

        Applies.ToThisAssembly();

        Actions
            .IncludeTypesNamed(x => x.EndsWith("Controller"));

        Routes
            .IgnoreControllerNamespaceEntirely();

        Views.TryToAttach(x =>
        {
            x.by_ViewModel_and_Namespace_and_MethodName();
            x.by_ViewModel_and_Namespace();
            x.by_ViewModel();
        });
    }
}

I have used the same conventions that Tim did in his example. He’s got a great explanation on what these do so I strongly recommend reading his post.

Running the application

At the time of this writing, there is no way to specify your default route in your registry. To get around this for now, I created a Default.aspx in the root and did a Response.Redirect(“~/Home/Index”) to force it to my default action. Once that’s in place, hit F5 and you should see this:

Figure1.3

Figure 1.3 – End result

That’s it! We’re all done. In my next part I’ll talk about wiring up forms to your controllers and leverage the UnitOfWork pattern to get the items saving.

Source code

You can download the source for this part at: http://svn.joshua-arnold.com/fubutodo/tags/part1

Technorati Tags: , ,

Comments 1 Comment

SnapDragon and Patterns at Neumont

7 06 2009

I was in Utah last week visiting Neumont University and had the privilege of talking with the student body about a variety of topics. I introduced the upcoming SnapDragon Framework and launched into a huge discussion regarding career advice and software development patterns. It was pretty unexpected but I honestly really enjoyed our talk. I wanted to spend some time reviewing some of the topics we went over.

Obviously I began with introducing myself and ProAce. Afterwards I went over the same basic presentation that I used for Austin Code Camp. I did elaborate on the examples, however, and gave a demonstration of a prototype of the application blocks that I have been ranting and raving about. I prototyped a Content Management System application block and gave a quick 5 minute example of how you could enable managed content in any MVC application. I will be following up with a post about this in my technical blog within the next few days.

The dialogue I had with the students regarding their careers, ProAce, and patterns was stimulating. We discussed implementation, the importance of patterns, and really just has a great back-and-forth about the reality of the industry. One of the more notable parts about this trip, however, was the wide variety of students that I met after the presentation. There was a large amount of interest in my company and I had the chance to meet a great number of prospective students. Very exciting!

I’m very grateful for my close relationship with the University and appreciate the opportunities they give me to speak and weigh in on important issues. I had a great time and was able to wrap up the final tasks with our enterprise project team (the primary focus of my visit).

For those of you that missed the presentation and would like to know what was covered, I will be doing a followup post very soon. Please check back for updates!


Technorati Tags: , , , , ,

Comments No Comments »

Adopting ASP.NET MVC: WebForms Damage Control

31 05 2009

I recently retweeted a comment from Brad Wilson about how much damage WebForms has done to the typical developer’s knowledge of the web. I thought he had summarized it quite while and I wasn’t going to offer my thoughts on the matter but it’s been on my mind for quite a while and I feel like it’s time for me to weigh-in on this situation. It turns out that I have quite a bit to say.

Before I begin, I will note that I have been working with WebForms since the early days and have truly appreciated the rapid rate in which the platform allows you to develop certain applications (or pieces of applications).

The individual adoption of ASP.NET MVC is no easy thing, so it should come as no surprise that an organizational adoption of ASP.NET MVC is not a walk in the park. The WebForms platform does a great job at providing a set of Rapid Application Development tools to get your job done quickly and somewhat easily…depending on the task and the team you’re working with. When you begin running into rendering problems or third-party integration problems, a deeper understanding of the toolset is required and unfortunately most entry and junior-level developers just don’t have it.

Individual Adoption

The transition probably isn’t the same from one person to the next, but I can make some generalizations based on my personal experiences introducing the change and helping through various online communities. These generalizations have been mostly made by helping people transition as they tackle more real-life applications using MVC. As with any new technology, I strongly recommend the “Hello, World!” approach but not everybody agrees with me.

I wanna go fast!

The initial introduction to the concepts seems to go over quite well. You’re not sure if they understand it or just want to get their hands dirty by playing with it. They’re not sure if they understand it either…but it’s a new technology and they’re excited to get their hands dirty.

Where’d my magic go?

The first question is asked: “How do I access my <some control ID> control? It’s not showing up in intellisense.” Be careful here, you’re about to tell them everything they know about the web is a lie!  You carefully deliver the bad news and show them some examples of posting information.

So…I have to reinvent the wheel?

This is a question that I get a lot and it’s taken me a while to understand how to respond to it. Now, it may be different with every person but I’ve found that this question gets asked for a few reasons that all seem to boil down to this: the purpose of the ASP.NET MVC framework has not been properly explained and a proper comparison between MVC and WebForms has not been made. It should be clear that MVC is an alternative to WebForms (not a replacement) and that the entire development approach is different.

“But I could do this in like 5 minutes in WebForms!”

Sure, that might be true. In fact, your organization might not be suited for MVC development and this might be the right time for you to realize that. However, if you’re like me then you know that while the ramp up time might be a little rocky, it’s going to benefit you in the long run. So you explain the differences one more time and kindly ask them to just “give it a shot.”

Where’d my magic go? (Again)

“OK, so then how would I access my <some control ID> control?”

Even though you’ve explained it quite a few times by now, you’re bound to be asked again. In fact, don’t be too surprised if this continues to happen for quite some time.

As with the adoption of anything, you need a roadmap. You need clear goals and a vision to follow. The road is rocky at first and you’re going to need that vision to keep you moving.

Organizational Adoption

The damage has moved far past just affecting developers. I think an interesting shift in management’s understanding of .NET has happened in the past year or two. As management gets more technically savvy and buzzwords come and go, it’s more important than ever that management understands the goals of the roadmap you’ve set out. If you’re about to pitch using a new development approach that requires intense training and ramp up time then you’d be better be prepared for battle.

Sounds simple…right?

Let’s take a look at an application that is ideal for WebForms development. We need an application for internal-use only that will take in some input through a couple of data entry screens, run some basic calculations, and display the data in some sort of table. Sounds simple, right? It sure is. It’s so simple that I can write the steps out as follows:

  1. Figure out what data needs to be entered and throw together a database
  2. Create some pages and drag over some textboxes
  3. Wire up the code and give it a test run
  4. Drag over a grid view and wire it up

That’s all there is to it. There was no real need for design, no complex requirements, or any real thought needed to make this work. You’re happy because it took you about an hour and management is happy is also because it took you about an hour. There’s no need to discuss the details on what’s actually happening under the hood because it gets the job done and does it quite well.

Can’t you just drag and drop something?

We can create simple applications very quickly and effectively. This is where we have been for a long time and management has caught on to that. They’ve undoubtedly heard the term “control” get thrown around on more than one occasion and probably understood it to mean something quite different than what it really is. You see, WebForms does such a damn good job at simplifying things that you can’t blame them for thinking that a control is more powerful than it really is. Just think about the wide variety of controls that we have at our finger tips:

  1. Simple textboxes
  2. Login controls
  3. Grid controls
  4. Reporting controls
  5. etc.

What’s my point?

We’ve created it all: the good, the bad, and the ugly. We’ve built application after application and have done our best to incorporate testing as part of our daily routine but it hasn’t been easy. We’ve developed frameworks to help us test faster, but our budgets haven’t been fond of creating and/or maintaining those tests. ASP.NET MVC provides a brand new way of approaching development. We can finally incorporate Test Driven Development at the presentation-logic level and begin giving a higher level of transparency into the stability of our applications. It’s pretty clear why a lot of us are excited about this.

The pushback that we will be experiencing will come from two levels: developers and management. You will be dealing with real issues when developers pushback if they’re responding the way that I’ve mentioned here. MVC is so different than the world that WebForms created so it’s important to understand the sociological aspect of this situation. Management might not want to invest in testing, but I think it’s pretty easy to combat that one. It’s just important to think about it before making your pitch.

Technorati Tags: , , , ,

Comments 1 Comment

CodeCamp and Car Wrecks

30 05 2009

Today I gave a presentation at Austin Code Camp on the up and coming SnapDragon framework. I honestly didn’t know what to expect from this year’s CodeCamp but from what I hear from other people, this wasn’t like most years. Regardless of the turnout, it was definitely a learning experience and an inspiration for me to get more involved with the development community at large. I’m definitely looking forward to getting more involved with the Austin Dot Net User’s Group and meeting some of the bigger names around Austin and growing my network.

If you’re wondering about the title of this post, a friend of mine and I were in a bit of a car wreck after leaving code camp today. It wasn’t anything too serious but unfortunately his car is a little banged up and I’m feeling pretty sore :( .

Anyway, I have attached the presentation for any one that is interested in looking over what was presented today.

SnapDragon Abstract:

A collection of tools, .NET classes, CSS classes, jQuery extensions, and other reusable application blocks that aim to minimize development overhead.

As illustrated in the attached presentation, we have future plans for growing the framework and involving the community in its development. I’m looking forward to releasing the current code base as Open Source within the next couple of weeks and reaching out to the community to get their feedback.

SnapDragon Framework FX (Download)

Technorati Tags: , , , , , ,

Comments 3 Comments

Austin Code Camp

29 05 2009

Somehow May managed to sneak right passed me…like a…ninja. Seriously, I have no idea where this month went. Do  you?

It’s 1:00am and I should definitely be in bed right now but I’m actually pretty excited for tomorrow morning. Not only is this my first time attending Austin Code Camp, I have the privilege of speaking on behalf of my company. I’m giving a presentation about our upcoming SnapDragon framework. I’m not sure if these sessions will be recorded, but I’ll be posting my material on here as a followup after tomorrow.

I know that I’ve promised to update more frequently…and I also know that I haven’t been doing that. I won’t make another promise of updating, but I’ll make a more conscious effort! Wish me luck :)

Technorati Tags: , , , ,

Comments No Comments »

The Social In Me

6 05 2009

I used to really care about being social. Actually, it’d probably be more accurate to say that I used to really care about making it seem like I tried to be social. Lately I’ve been learning to kind of embrace a lot of my quirks and stop trying so hard. I probably try to “redefine myself” on a daily basis but this time I think I’m on to something…

We’ve been working on the social.proace project at work and it’s finally starting to take off. It’s funny, no matter how cliche it is, every initiative we make always follows that damn hype cycle:

Hype Cycle

Hype Cycle

(source: Wikipedia)

I’d like to think we’re at that lovely plateau, but I think we’re more into that slope of enlightment. There are some bugs to work out with our twitter integration and of course the random bugs here and there, but I think it’s coming together pretty nicely. Then of course there’s the whole issue of having content…so I’m going to have to stop being lazy and start writing some technical posts.

It’s not quite ready to link to yet, but I’ll give you a little preview:

social.proace

social.proace

OK, I had planned to make a long post but I’m exhausted. I’m going to throw on some Arrested Development and The Office and head to bed.

Technorati Tags: , , ,

Comments No Comments »

Acquired Tastes

17 04 2009

Coffee is an acquired taste. Most people love the aromas in the air but cannot quite appreciate the taste. I say they’re missing out on the addictive caffeine high that I’ve grown to know and love.

Sub-cultures tend to multiply at an astounding rate and the pastimes change so quickly that it makes my head spin. We have coping mechanisms for our inability to keep up: “It’s a trend,” “It’s a phase.”  Surely these are true, but I can’t help but wonder if they’re at least partially self-fulfilling prophecies.

I’m almost tempted to question my uniqueness in this world when someone greets me with some sort of gesture that I could’ve sworn I created. It’s mind boggling to consider the odds of both of us creating some new greeting and meeting each other. While it helps make this world feel a little smaller, it certainly does not help demystify the complexity of this universe we live in.

I think we drastically underestimate the complexity of our lives and the decisions that we make on a daily basis. I know this because my job is to model these decisions and automate them whenever possible. Believe me; you’d be surprised how much information is needed to make even a simple and objective decision.

Personally, I believe there’s a beauty in the art of modeling these complex decision making processes. The art and perhaps the underlying creative process is obviously vastly different than their traditional understanding, but it’s hard to argue the comparison when you’ve worked with some of the people I’ve had the privilege of assisting. Each of these individuals has had their own special way of approaching situations and creating the designs that help piece the puzzle together.

Problem solvers, designers, happy-hour groupies, and poets; I’m surrounded by representatives of so many of these sub-cultures and there are only 15 people in my immediate view. A man to my left is developing something in Java and the women to my right are blowing off some steam from what was obviously a tough week. No, I wasn’t eavesdropping; I believe the shots of tequila were a clear enough sign.

Some come for the atmosphere, some enjoy the company, and others just didn’t feel like sitting at home. Everybody here has their own take on the world and it’s always so different. I mention the obvious because sometimes I find myself wrapped up in my own world and I find myself spending my time modeling decisions instead of making them.  In fact, I get so wrapped up in what I’m doing that I get a little lost.

Sometimes I wonder what it would be like to strike up a conversation and say whatever’s on my mind. It’d probably go something like this:

I enjoy a nice cup of coffee, I spend too much time trying to understand everything, and someday I’ll grow up and be a little more grounded. What’s your story?

Technorati Tags: , , , , ,

Comments 4 Comments

Getting in Touch

28 03 2009

There’s too much pressure. Pressure to expand on the summarizing statements that I always begin with and to close with some enjoyable sentiment that lives up to some mysterious expectation. It’s almost like I feel the need to cater my words to what I think people are expecting to read. Meanwhile, I secretly want to tell you all about how beautiful the weather is and why I’m content with not having anything “exciting” planned for today. I’ve never been one to over analyze this type of thing – certainly not enough to force me into some sort of writer’s block. I honestly think I’m just really out of touch lately.

I’ve been working on some new initiatives at work that are really engaging and it’s been lot of fun. I’m leading our new open-source initiatives and we’re all working hard to polish off the first release of our upcoming Framework (code-name “SnapDragon”) in time for the Austin CodeCamp this May. I’m also working with our Creative Director and my friend Rex to revamp our website and create a company blog as well. Needless to say, I’ve been pretty busy.

I wish I could explain to you everything that has happening with me recently but I don’t know where to start. I’ve been really contemplating some heavy topics, each of which I could probably write a few pages and probably will in the upcoming weeks. Everything from tacit knowledge to reflecting over this past year, there’s a lot to talk about. Undoubtedly, you’ve seen the new look and feel of my blog and you’ve noticed a sudden change in the tone of my writing. I want to introduce the new tone with a few posts and really get back to writing more often – even if it’s a random thoughts that I had while I was driving to work.

In the meantime, play around a little and discover some of the posts that you may have missed. I’ve had plenty of these posts written and let them sit around since I never had a place for them but with the new direction of my blog, I’ve included them in the various categories. I hope you’ll take the team to read a few and open up some discussion. You know how much I love to talk about these things.

[test]

Technorati Tags: ,

Comments 1 Comment