Category Archives: CodeProject

ASP.Net WebForms can do FriendlyUrls too!

With the ASP.Net 4.5 release, we saw the addition of declarative model binding, value providers, unobtrusive validation, and model state. While some of these features were a carry over from the MVC framework, we were still missing that cool SEO friendly friendly URL format.  With the release of ASP.Net 2012.2, Microsoft has delivered on that need.

The FriendlyURLs feature can be added to any ASP.Net WebForms 4 project through the installation of a NuGet package with the command:


Install-Package Microsoft.AspNet.FriendlyUrls -Pre

If you are not currently using routing in your webforms project, you will need to add a call to configure the routing.  If you are familiar with MVC, you should recognize this snippet in global.asax.cs:

RouteConfig.RegisterRoutes(RouteTable.Routes);

The content of your RouteConfig.cs file in the App_Start folder should look something like:

public static class RouteConfig
{
  public static void RegisterRoutes(RouteCollection routes)
  {
    routes.EnableFriendlyUrls(new FriendlyUrlSettings()
    {
      AutoRedirectMode = RedirectMode.Permanent,
      CachingMode = CachingMode.Dynamic
    });
  }
}

I have added the configuration options to make redirects from the FriendlyUrl routes permanent (Http 301 status code) and the dynamically cache the contents of the output.  With this configuration in place, I can now route my users to cool looking and SEO friendly URLs like:

I know what you’re thinking:  Jeff, in MVC I get the entries from the request submitted through to action methods as input parameters, how do I access these values from a webform?  The implementation of FriendlyUrls includes an extension method for the Request object called GetFriendlyUrlSegments that returns an IList<string>.  That’s nice, if you really want to iterate over the entire URL and parse apart what was submitted, but I think there is something else you would prefer.

Enter the ValueProviders

ValueProviders are parameter-level attributes that can be used to decorate methods in your webforms.  Using a FriendlyUrlSegments attribute, I can configure a public method in my webform to provide content based on the values submitted on the URL.  Conside this simple webform:

I can use a new feature in ASP.Net 4.5 and bind my Product business object directly to the FormView control.  All I need to do is specify the ItemType and SelectMethod properties to bind data for read operations.  ItemType is the qualified name of the class that is being bound.  SelectMethod is the public method in the code-behind that will return the business object (Product in this sample) to be presented.  Note that I am using the Item keyword to bind to the Product.  This creates a one-way binding, similar to how we use the Eval keyword.  There is also a BindItem keyword available that performs the familiar two-way binding that the Bind keyword gives us.

Let’s look at the GetProduct method:

public static readonly List ProductList = new List()
{
  new Product
  {
    Id=1,
    Name="Chess",
    Description="The classic game - you know... Chess!",
    Price=9.99M
  }
};

public Product GetProduct([FriendlyUrlSegments]int? id)
{
  if (id == null)
    return null;

  return ProductList.FirstOrDefault(p => p.Id == id.Value);


}

Now we see how the FriendlyUrlSegments value provider is put to use.  When the webform is rendered, at the time that my FormView is ready to bind to data.  I don’t need to fuss with event timings, postbacks, or viewstate.  The webform will pass the parameters appropriately from the FriendlyUrl as an input parameter when the FormView is ready to be rendered.  In this case, I end up with a simple webpage that tells me about the Chess product:

Summary

In this article, we introduced the concept of FriendlyUrls in ASP.Net webforms.  I showed you how to retrieve data from the formatted URL string so that it can be consumed.  We also took a brief look at ValueProviders in ASP.Net 4.5 and used the FriendlyUrls attribute with some declarative data-binding on a standard FormView to present some data.

Next time, we’ll dig further into ValueProviders and ModelBinding in ASP.Net 4.5

WebAPI follow-up.. what about protocol buffers?

As a follow-up to my previous article about WebAPI, I received an interesting question from a friend on LinkedIn:  Is there a way to emit data in Protobuf format?  

This one got me thinking…  ProtoBuf is short for Protocol Buffers which is a data format technology invented by Google to allow for maximum compression of structured data that can be interpreted by any programming language.  Google says: “think XML, but smaller, simpler, and FASTER”, and I think they may be right.  According to Google’s own benchmarks, Protocol Buffer format is 3-10x smaller and 20-100x faster on the wire.

Fortunately, there is a pair of NuGet packages available to help our WebAPI application handle the protocol buffer format.  From within my WebAPI application, I can use the NuGet console to install these two packages with the following command:

Install-Package WebApiContrib.Formatting.ProtoBuf

Once this command completes, the protocol buffers library protobuf-net should be installed as well as the appropriate media formatter in the WebApiContrib.Formatting.ProtoBuf library.  The next step to configure WebAPI is to configure the application to be aware of the new formatter.  This is accomplished with the following command in Application_Start in the global.asax.cs file:

        protected void Application_Start(object sender, EventArgs e)
        {

            GlobalConfiguration.Configuration.Formatters.Add(
				new WebApiContrib.Formatting.ProtoBufFormatter());

            RegisterRoutes(RouteTable.Routes);
        }

Finally, we need to configure the data we are going to transport so that the Protocol Buffers library knows how to serialize it.  This is accomplished with a series of attributes on our Team class:

    [ProtoContract]
    public class Team
    {
        [ProtoMember(1)]
        public string City { get; set; }

        [ProtoMember(2)]
        public string Name { get; set; }
    }

The ProtoContract attribute tells the serializer that this class can be formatted with protocol buffers.  The two ProtoMember attributes assign a location to the properties in the resultant stream of data.  The numeric locations identifiers should be continuous and start with one.

With this minor bit of configuration, we can start our application and open Fiddler to easily test our new protocol buffer aware API.  If I point Fiddler to the standard location http://localhost:4829/api.my I’ll get normal JSON output:

[
{"City":"Philadelphia","Name":"Phillies"},
{"City":"Boston","Name":"Red Sox"},
{"City":"Cleveland","Name":"Browns"},
{"City":"Houston","Name":"Astros"},
{"City":"San Diego","Name":"Chargers"}
]

but if I go into the Composer tab of Fiddler and submit the request with an accept header of application/x-protobuf:

The results returned are the protocol buffer format, not entirely visible to us as text:

We didn’t change any of our business logic.  We didn’t change any of our data access code.  By simply adding an additional media formatter, WebAPI was able to handle and format the resultset as requested.  What else could you format with WebAPI?  What other request formats could you interact with?  Let me know what you think in the comments below.

The Truth About HttpHandlers and WebApi

In 2012 when Microsoft introduced the ASP.Net WebAPI, I initially became confused about yet another way to generate and transmit content across a HttpRequest.  After spending some time thinking about WebAPI over the past week or two and writing a few blocks of sample code, I think I finally have some clarity around my confusion and these are those lessons I learned the hard way.

Old School API – SOAP and ASMX

When ASP.Net launched, the only framework to create and publish content that was available was WebForms.  Everybody used it, and it worked well.  If you wanted to create an interface for machine-to-machine communications you would create a SOAP service in an ASMX file.  You could (and still can) add web references to projects that reference these services.  The web reference gives you a first class object with a complete API in your project that you can interact with.

Over time, it became less compelling to build the SOAP services as they transmitted a significant amount of wrapper content around the actual payload you wanted to work with.  This extra content was messy, and other programming languages didn’t have the tooling support to manage them well.  Developers started looking for less complicated alternatives to communicate with connected clients.  It turns out that there was another option available that was overlooked by many: HttpHandlers.

HttpHandlers in ASP.Net

HttpHandlers are the backbone of ASP.Net.  An HttpHandler is any class that implements the IHttpHandler interface, handles an incoming request, and provides a response to webserver communications.  IHttpHandler is a trivial interface with the following signature:

 

    public class TestHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            // TODO: Implement this method
            throw new NotImplementedException();
        }

        public bool IsReusable
        {
            get
            {
                // TODO: Implement this property getter
                throw new NotImplementedException();
            }
        }
    }

Listing 1 – Simple IHttpHandler interface implementation

The ProcessRequest method is where we can insert our code to handle all data being requested of the handler and all data to respond from the handler.  The IsReusable property is often misunderstood, but has a simple definition: Should an instance of this handler be re-used for other requests to the webserver?  If the return value is false, a new version of the handler class will be instantiated for each request to the server.  If IsReusable returns true, then the same instance will be used for all requests to the server.  A simple implementation that outputs some of my favorite sports teams in XML format might look like this:

public class MyHandler : IHttpHandler
    {


        public readonly static IEnumerable MyTeams = new List
        {
            new Team { City = "Philadelphia", Name = "Phillies" },
            new Team { City = "Boston", Name = "Red Sox" },
            new Team { City = "Cleveland", Name = "Browns" },
            new Team { City = "Houston", Name = "Astros" },
            new Team { City = "San Diego", Name = "Chargers" }
        };

        #region IHttpHandler Members

        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/xml";

            context.Response.Write("");

            foreach (var t in MyData.MyTeams)
            {
                context.Response.Write("");
                context.Response.Write("");
                context.Response.Write(t.City);
                context.Response.Write("");
                context.Response.Write("");
                context.Response.Write(t.Name);
                context.Response.Write("");
                context.Response.Write("");
            }

            context.Response.Write("");

        }

        #endregion
    }

Listing 2 – A Sample XML Generating HttpHandler

Some developers get confused about the difference between a class that implements IHttpHandler and an ASHX file.  These are very similar things, but different with their own advantages.  An ASHX file is an IHttpHandler whose end-point is defined by the name of the ASHX file.  If you create foo.ashx you can trigger the ProcessRequest method by navigating to http://localhost/foo.ashx  Conversely, a class that implements IHttpHandler needs to be registered in web.config with a path and file-extension end-point to answer to.  The format of this registration lies within the system.webServer section of web.config:


    

        

    
  

Listing 3 – Web.Config entries for an HttpHandler

Further instructions on how to configure the HttpHandler is available on MSDN

Both of these types of HttpHandlers are easy to implement, and put you right on the metal.  You are handed and HttpContext and you can do anything you want to handle the incoming request and respond to it.  If you are working with some low-level operations such as inspecting HttpHeaders for responses, you may choose to go this route.

ASP.Net WebAPI in a nutshell

Scott Guthrie describes ASP.Net WebAPI as a technology that allows developers to build powerful web based APIs that have the following 9 core capabilities:

  • Modern HTTP Programming Model – Directly access the HTTP requests and responses using a strongly typed HTTP object model
  • Content Negotiation – Built in support for content negotiation, which allows the client and server to work together to determine to correct data format to return from an API
  • Query Composition – Easily supports OData URL conventions when you return a type of IQueryable<T> from a method
  • Model Binding and Validation – Supports the same model binding available in WebForms and MVC
  • Routes – You know ’em, you love ’em… they’re all over ASP.Net.  Why WOULDN’T you expect them in WebAPI?
  • Filters – You can apply similar filters to those that you create for MVC
  • Improved Testability – WebAPI uses two new objects called HttpRequestMessage and HttpResponseMessage to make unit testing significantly easier.  
  • IoC Support – The same service locator pattern implemented in ASP.Net MVC is available to inject dependencies into your API implementations
  • Flexible Hosting – Web APIs can be hosted within any type of ASP.Net application.  You can also host it within any process if you don’t want to engage IIS to host your API.

The quick and dirty summary of this list is that WebAPI functions, looks, and feels like developing with ASP.Net MVC.  There are a few simple differences between them, but once you learn them, its very easy to progress quickly.

The Super-Duper Happy Sample

To implement a simple service that outputs a collection of values when a GET is called with no arguments (eg: http://localhost/api/Values ) you need to add very little to get started.  First, add a WebAPI controller to your website by selecting the appropriate item in the Add Item dialog box:

The default implementation delivered looks like the following:

    public class ValuesController : ApiController
    {
        // GET api/<controller>
        public IEnumerable<string> Get()
        {
            return new string[] { "value1", "value2" };
        }

        // GET api/<controller>/5
        public string Get(int id)
        {
            return "value";
        }

        // POST api/<controller>
        public void Post([FromBody]string value)
        {
        }

        // PUT api/<controller>/5
        public void Put(int id, [FromBody]string value)
        {
        }

        // DELETE api/<controller>/5
        public void Delete(int id)
        {
        }
    }

Listing 4 – Default API Controller implementation

With this simple implementation you can navigate your browser to http://localhost/api/values and get the two values “value1” and “value2” returned in XML format.  If you programatically request that address and change your “accept” header to “text/json” you will receive JSON in the response stream.  You can PUT, POST, and DELETE content to this controller and the appropriate action methods will be triggered based on the HTTP action executed.  

The Truth

WebAPI is just another HttpHandler, one that has been wrapped around all sorts of helpful content management and negotiation technologies as Scott Guthrie suggested.  You certainly could cut out all of those other technologies and benefits and code directly “on the metal” with an HttpHandler, and that was the only choice you had until 2012 and ASP.Net 4.  In today’s web community, its best to use RESTful methods and use a standard format like XML or JSON to transfer data.  HttpHandlers had their place, but going forward, I’m going to use WebAPI whenever I need to transmit data over HTTP to browsers or any other connected client.

Adding SignalR to an ASP.Net WebForms Project

In this post, I want to demonstrate how you can add real-time interactivity to a website that is written with ASP.Net WebForms.  I’m going to show you how easily and quickly you can have SignalR update the content of a standard ListView webcontrol to show a ‘live’ log of events on your webserver.

UPDATE Jan 28, 2013: I have uploaded the source to this post to my GitHub account.  Go get some sample code!

Project Setup

For this sample, I’m going to start up a standard ASP.Net WebForms application with .Net 4.5  Once the project is created, I’m going to clear out the default content on the home page and insert my ListView control as shown in Code Listing 1:

<asp:content runat="server" id="BodyContent" contentplaceholderid="MainContent">

    
    <h3>Log Items</h3>
    <asp:listview id="logListView" runat="server" itemplaceholderid="itemPlaceHolder" clientidmode="Static" enableviewstate="false">
        <layouttemplate>
            <ul id="logUl">
                <li runat="server" id="itemPlaceHolder"></li>
            </ul>
        </layouttemplate>
        <itemtemplate>
            <li><span class="logItem"><%#Container.DataItem.ToString() %></span></li>
        </itemtemplate>
    </asp:listview>

</asp:content>

Code Listing 1 – Initial layout of the ListView

With this block configured, I can write a small snippet in the code-behind default.aspx.cs file to load some log information from some other datastore.  For this example, I’m just going to bind to a List of strings to demonstrate the mix of server rendered content and dynamically added content:

        protected void Page_Load(object sender, EventArgs e)
        {

            var myLog = new List();
            myLog.Add(string.Format("{0} - Logging Started", DateTime.UtcNow));

            logListView.DataSource = myLog;
            logListView.DataBind();

        }

Code Listing 2 – Code behind to load some rendered data

I should now be able to start my application and see a simple pair of lines added to the default page that appear something like this:

Figure 1 – Static Log Content

Adding SignalR to the mix

At this point, we need to add SignalR to our project through the NuGet package manager.  I prefer to use the ‘Manage NuGet Packages’ dialog window to add these packages, so I add the packages for SignalR by searching for and adding the “Microsoft ASP.Net SignalR JS” package and the “Microsoft ASP.Net SignalR SystemWeb” packages.  At the time of this article’s writing, these packages are available in the ‘Pre-Release’ state only.

When you craft code for SignalR, you need to write client-side and server-side code.  The server-side code in this sample will be housed in a SignalR Hub.  

A hub is a structure that facilitates simple communications to a collection of client systems that are listening for commands to execute.  

In this project, we will create a LogHub class in c-sharp that will allow log messages to be communicated to all listening client browsers.  To simulate the repeated creation of log messages, I will use a timer to periodically transmit messages.  The code for the LogHub.cs file appears below:

    public class LogHub : Hub
    {

        public static readonly System.Timers.Timer _Timer = new System.Timers.Timer();

        static LogHub()
        {
            _Timer.Interval = 2000;
            _Timer.Elapsed += TimerElapsed;
            _Timer.Start();
        }

        static void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            var hub = GlobalHost.ConnectionManager.GetHubContext();
            hub.Clients.All.logMessage(string.Format("{0} - Still running", DateTime.UtcNow));
        }

    }

Code Listing 3 – LogHub source code

This hub contains 1 static event handler to listen for the timer’s elapsed event.  The first statement of this method gets a reference to the singleton LogHub that is running in our web server.  The next statement issues a message to all clients using the “hub.Clients.All” structure.  This is an interesting piece of code, as the “All” property of “hub.Clients” is a dynamic object.  The “All” property is a proxy for the objects and their methods that are available to be called over the SignalR pipeline.  

The object that is called over the SignalR pipeline by “All” from Code Listing 3, will be constructed in JavaScript and exposed to our hub in the following script block:

<script src="Scripts/jquery.signalR-1.0.0-rc1.min.js"></script>
<script src="/signalr/hubs" type="text/javascript"></script>
<script type="text/javascript">
    
    $(function() {

        var logger = $.connection.logHub;

        logger.client.logMessage = function(msg) {

            $("#logUl").append("<li><span class="logItem">" + msg + "</span></li>");

        };

        $.connection.hub.start();

    });

</script>

Code Listing 4 – JavaScript to log messages

There are a few pre-requisites here that I need to discuss first.  In my sample project, there is a reference to the jQuery library in my layout page, if you do not have that reference available, I recommend you add it to simplify the volume of JavaScript code you will be writing.  Next, I have added script references to the “jquery.signalR” library to give us access to the SignalR functionality.  The second reference is to a “magic URL” at /signalr/hubs.  This is a virtual location that exposes the methods that the hub classes have in public scope.  These methods can be called from the browser, through the plumbing established in this file.

The JavaScript in this listing is a normal jQuery enclosure, to ensure its contents do not get executed until after jQuery is loaded.  The first line gets a reference to the LogHub object that we created in listing 3.  We then connect a function to our client’s “logMessage” property.  This is the same function that is referenced in listing 3’s dynamic “All” object.  Thankfully, since we marked our ListView object with a static ClientIDMode and disabled ViewState, there are no hoops for us to go through to get a reference to the DOM objects that were created.  In this case, we’re simply going to append a list item (LI) to the unordered list (UL) with the message submitted to the function.  The last line of this enclosure is very important.  The start() method must always be called in order for SignalR to know to start listening for invocations from the server.

Before we can run our sample, we need to add one last piece of plumbing.  We need to tell the server to expose that magic URL at “/signalr/hubs”.  This is accomplished by adding a line to the Application_Start event handler in global.asax.cs:

void Application_Start(object sender, EventArgs e)
{
    // Code that runs on application startup
    BundleConfig.RegisterBundles(BundleTable.Bundles);
    AuthConfig.RegisterOpenAuth();

    RouteTable.Routes.MapHubs("~/signalr");

}

Code Listing 5 – Mapping the Hubs Route

Once this line is added, we can start our application.  You should see a result screen similar to the following:

Figure 2 – Static and Live Log Content

Summary

With a few simple settings on our WebForms project and the controls I wanted to modify for this sample, I was able to dramatically simplify the SignalR interactions with WebForms.  In this case, I didn’t run into any issues with the parts of WebForms that developers try to avoid like PostBack, Page Lifecycle, ViewState, and ClientID rendering.  Next time, we’ll confront those issues head on as I’ll show you how to interact between SignalR and controls that post back to the server.

ASP.Net AJAX Controls for MVC developers

I’ve been spending a bit of time over the past month looking into ALL of Asp.Net, not just MVC or SignalR or WebAPI.  I’ve even gone back and spent some time with good old WebForms.  When we think back to ASP.Net webforms, many of us cringe and get upset when we think about ViewState, ClientId naming, and PostBack processing.  However, I think there are some really good things in WebForms that many of us need to be reminded of.  Let’s take a look at a quick sample, comparing a “modern” MVC AJAX approach to a web page being built with the tried and true WebForms approach.

Our Data Sample

To start, I’m going to create an MVC4 project in Visual Studio and add the EntityFramework.SqlServerCompact nuget package.  With this in place, I created a Product object and a simple data context using Entity Framework Code First:

    public class Product
    {

        public int ProductId { get; set; }
        public string Name { get; set; }
        public string Manufacturer { get; set; }
        public decimal Price { get; set; }

    }

    public class ProductContext : DbContext
    {
        public DbSet Products { get; set; }
    }

    public class ProductDbInitializer : DropCreateDatabaseAlways
    {

        protected override void Seed(ProductContext context)
        {

            var checkers = new Product()
            {
                ProductId = 1,
                Name = "Checkers",
                Manufacturer = "Milton Bradley",
                Price = 5.99M
            };
            context.Products.Add(checkers);

            var chess = new Product
            {
                ProductId = 2,
                Name = "Chess",
                Manufacturer = "Parker Brothers",
                Price = 8.99M
            };
            context.Products.Add(chess);

            var backgammon = new Product
            {
                ProductId = 3,
                Name = "Backgammon",
                Manufacturer = "Hasbro",
                Price = 12.99M
            };
            context.Products.Add(backgammon);

            var connectFour = new Product
            {
                ProductId = 4,
                Name = "Connect Four",
                Manufacturer = "Milton Bradley",
                Price = 7.49M
            };
            context.Products.Add(connectFour);

            base.Seed(context);
        }

    }

Note the DbInitializer class – this will create some seed data for us to display in this sample.  

The ‘standard’ MVC approach

Typically, an MVC view will output a grid of data with a simple for-loop over some data.  With the razor-formatting engine, this becomes a trivial piece of work in our view:

        @foreach (var product in Model)
        {
            
                @Html.ActionLink(product.Name, "Details", new { id = product.ProductId })
                @product.Manufacturer
                @product.Price.ToString("$0.00")
            
        }

This is a simple approach that dumps the data to screen.  For a prototype it may be enough, but in today’s web, we want something more dynamic.  We’d prefer an AJAX solution that allows us to search, sort and flip through pages of content.  

If you’re like me, you went looking for a jQuery plugin to do some of this work for you.  There are a number of great grid controls available like KendoUI or jqGrid.  I first used the jqGrid and ran into configuration after configuration that was undocumented that I needed to test and ensure behaved the way I needed it to.  Then I had to chase down a handful of additional methods I needed to add to my MVC controllers to perform the search and sort operations.  After that, I had to update those controller methods to handle paging.  All in all, I ended up adding more server-side methods in my MVC controller and writing client-side javascript wrapper methods for the grid control to simplify my data access needs.  I could move my data searching and sorting methods on the server-side into an ApiController, but my problem remains the same: I’m writing a lot of code to massage the data in the grid to fit my user’s needs.

The WebForms AJAX way

If we go back to the WebForms technique using the standard gridview control, we could directly databind to our Products DbSet in our context.  Our construction steps would be as simple as this:

    1. Drag a LinqDataSource onto the webform designer surface
    2. Configure the LinqDataSource to connect to our ProductContext and use the Products dataset
    3. Drag a GridView onto the webform designer surface
    4. Connect the GridView to the LinqDataSource
    5. Configure the GridView to enable paging and sorting

    Done.. I’ve written no code, and using only the designer tools I have a functional grid. However, this doesn’t have the cool filtering and AJAX features like our jqGrid.  We can theme the GridView easily, but its still not quite… polished.  We would need to add additional controls to the page and additional code to connect some filter controls to the grid.

    Enter a commercial grid

    A commercial grid control will take our capabilities on the WebForm even further, with even less work.  Let’s consider the Telerik ASP.Net Grid… With Telerik’s grid, we need to configure our web project to handle the Telerik controls by adding some entries into web.config.  These can be copied directly from another Telerik ASP.Net controls  project.   Once those settings are in place, adding and configuring our grid is as simple as:

    1. Drag a LinqDataSource onto the webform designer surface
    2. Configure the LinqDataSource to connect to our ProductContext and use the Products dataset
    3. Drag a RadGrid onto designer surface
    4. From the RadGrid smart-tag, click the linkbuttons to add the RadScriptManager and RadAJAXManager
    5. From the RadGrid smart-tag click the checkboxes to turn on the sorting, filtering, and paging functionality as desired
    6. From the RadAJAXManager smart-tag, click the checkboxes to enable the RadGrid as an AJAX trigger and enable the RadGrid as an AJAX target

    Without any coding, using just the designer tools, I’ve completed the task again.  We now have a grid that will sort, page, and filter on AJAX queries back to the website. Additionally, we can turn on other cool functionality that is built in to the control like “Column Grouping”, “Export to Excel” or “Export to PDF”.  Once again, no coding is needed to enable this functionality.  The volume of features in these commercial controls is amazing, and they really are worth it when you look at the time it would take you to build each of these features.  There are significant templating and coding options available if you want to turn this Telerik grid into something more unique for your customer.

    Summary

    The amount of hand coding and mix of client and server side coding to make an MVC solution work just takes too much of my valuable development time.  I can put together a great looking webpage very quickly using a WebForms approach.  Finally, I can make that WebForm really shine if I use a set of controls from a vendor that are really polished.  Asp.Net developers, don’t limit your projects to just MVC or WebForms.  Try mixing and matching capabilities to make your development tasks easier.

    kick it on DotNetKicks.com