Friday, June 29, 2012

Building Single Page Apps with ASP.NET MVC4 - Part 5 - using ASP.NET MVC4 RC

This blogpost is part of a series about building Single Page Applications using ASP.NET MVC 4. Parts 1 through 4 use ASP.NET MVC4 beta but this post will focus on building SPA after upgrading to ASP.NET MVC 4 RC

  1. Single Page Applications - Part 1 - Basic Desktop application
  2. Single Page Applications - Part 2 - Advanced Desktop Application
  3. Single Page Applications - Part 3 - Basic Mobile Application
  4. Single Page Applications - Part 4 - Sorting, Filtering and Manipulation data with upshot.js
  5. Single Page Applications - Part 5 - Using ASP.NET MVC4 RC

In the release candidate for ASP.NET MVC 4, Microsoft decided to remove the Single Page Application template and supporting files. This was no surprise as I already concluded in part 4 of this series that SPA was far from being ready.

So how do you build an SPA after installing MVC4 RC? Peter Porfy described one solution on his blog. Essentially he is taking the latest nightly build of ASP.MVC4 and correcting the errors. The downside to his solution is that it looks like a lot of work and this is still very unstable.

The key to my solution lies in the fact that SPA written against ASP.NET MVC4 beta, still run on machines with only the MVC4 RC installed. All the SPA essentials can be found in NuGet packages. As it happens, I had created a new MyGet feed to enable NuGet Package Restore so I didn't have to include the SPA packages every time I released new code on my blog.

I realize that this means you are not really using the MVC4 RC but at least you can continue building SPA after installing it until Microsoft releases a new and supported version.

Starting a new SPA using VS 2010 with ASP.NET MVC 4 RC

Follow these steps to get started:
ASP.NET SPA MVC4 Beta feed

Create a new MVC project and install the SPA NuGet packages from the PM Console:

  • Menu --> File --> New --> Project --> ASP.NET MVC4 Web Application --> Internet Application
  • Find the packages.config file and remove the line that includes the "EntityFramework". This version 5.0.0-rc RC is incompatible with the version 4.3.1 that we need. The line looks like this:
<package id="EntityFramework" version="5.0.0-rc" targetFramework="net40"/>
  • Go to your project references and remove the reference to EntityFramework here as well. Below screenshot shows both action:
  • Menu --> Tools --> Library Package Manager --> Package Manager Console

Make sure that 'Package source:' points to my MyGet feed otherwise you will download the packages from the official not-supported NuGet source

Install-Package SinglePageApplication.CSharp
Install-Package EntityFramework

After closing and re-opening the solution you're now ready start developing a new SPA

Migrating DeliveryTracker to MVC4 RC

I took the source code of DeliveryTracker4 from my SkyDrive and used this as a starting point for a new project that I also conveniently had called 'DeliveryTracker' to minimize namespace problems

  • This is the list of files and folders I copied and included into the new 'DeliveryTracker' project.
    .\DeliveryTracker\Content\Site.mobile.css
    
    .\DeliveryTracker\Controllers\DataServiceController.cs
    .\DeliveryTracker\Controllers\ViewSwitcherController.cs
    
    .\DeliveryTracker\Models\AppDbContext.cs
    .\DeliveryTracker\Models\DomainModel.cs
    
    .\DeliveryTracker\Scripts\App
    
    .\DeliveryTracker\Views\Home
    .\DeliveryTracker\Views\Shared\_Layout.cshtml
    .\DeliveryTracker\Views\Shared\_Layout.mobile.cshtml
    .\DeliveryTracker\Views\Shared\_SpaScripts.cshtml
    .\DeliveryTracker\Views\Shared\_ViewSwitcher.cshtml
    
  • I changed the connectionstring in both the connectionStrings and entityFramework sections of the web.config file
    <connectionStrings>
      <add name="DefaultConnection" providerName="System.Data.SqlClient" connectionString="Data Source=.;Initial Catalog=DeliveryTracker;Integrated Security=SSPI" />
    </connectionStrings>
    
    <entityFramework>
      <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
        <parameters>
          <parameter value="Data Source=.; Integrated Security=True; MultipleActiveResultSets=True" />
        </parameters>
      </defaultConnectionFactory>
    </entityFramework>
    

There were some additional steps to take before I could get my DeliveryTracker sample to function correctly.

  • Update the global.asax.cs file to add my database initializer to the Application_Start function
    #if DEBUG
    System.Data.Entity.Database.SetInitializer
      (new DeliveryTracker.Models.AppDbContextSeedInitializer());
    #endif
    
  • Update the site.css file to include the two classes that were added to highlighted changes on screen
    .delivered
    {
        text-decoration: line-through;
        color: #008000;
    }
    
    .updated
    {
        background-color: #FFFF00;
    }
    
  • Update the RouteConfig.cs file to make the DataService controller reachable to accept POST'ed updates.
    routes.MapHttpRoute(
      "DataService", // Route name
      "api/DataService/{action}", // URL with parameters
      new { controller = "DataService" } // Parameter defaults
    );
    

Download the source code

You can download the source code from my SkyDrive (Visual Studio 2010 project)

23 comments:

Bart Jolling said...

I heavily edited above post after finding a more streamlined process. I also created a dedicated MyGet feed for this to keep it self-contained. I will remove the MyGet feed that I originally used for this post

Anonymous said...

Works fine! Thank you!

Anonymous said...

Hi, just downloaded DeliveryTracker from your SkyDrive and when I try and run it I get an AddWithoutValidation error when making the initial GetDeliveriesForToday method call. Any idea what I am doing wrong?

Thanks

Bart Jolling said...

Have you by any chance installed .NET Framework 4.5?

http://forums.asp.net/t/1808094.aspx/1

Anonymous said...

Hi Bart, yes - should have done a search on that before posting. Do you know if your updated DeliveryTracker solution would run in Azure?

Thanks,

Bart Jolling said...

Thanks for giving me this kind of feedback. I wasn't aware it didn't run on .NET 4.5 until you asked.

It does run on Azure though. I have deployed this on the new 'Azure Web Sites' service: DeliveryTracker on Azure

Read my post on how I published it

Nuri said...

Hi Bart, I downloaded your DeliveryTracker5.zip and found alot of the files were marked as read-only. I fixed this and changed the connectionstring to "Data Source=.\SQLExpress;Initial Catalog=DeliveryTracker;Integrated Security=SSPI" but I'm still getting "Failed to get the MetadataWorkspace for the DbContext type 'DeliveryTracker.Models.AppDbContext'." on the home Index view, on the Html.UpshotContext() call... I am running VS2010 SP1, MVC4 and SQLExpress 2008. Anything srping to mind? Cheers, Nuri (Sydney)

Bart Jolling said...

Did you set it in both the 'connectionStrings' and the 'entityFramework' sections in the web.config. And did you set the name of the connection string to "AppDbContext"?

When you receive that kind of exception, the underlying issue is reported in the 'inner exceptions'. Navigate these and you'll find the original error message.

Mathieu Coddens said...

Hi Bart,

I read your blog about MVC and SPA beta. I wanted to test it and I tried with your feed and what you explain but on VS2012RC. I don't succeed to create a really simple SPA app, still have package conflict. Actually, I'd the System.Json package missing, so I installed it and then I'd the System.Web.Http.Common package missing, I installed it but after I'd a package conflict. The RouteParameter method exists in 2 dlls (...common.dll and System.Web.Http.dll) and I think I need both. Do you have an idea ? Or I can't try SPA anymore or it doesn't work on VS2012RC at all ?
Thanks in advance ;o)

Bart Jolling said...

Hi Mathieu,

I haven't tried with VS2012 yet. With the omission of SPA I didn't really feel compelled to upgrade.

What I would try:
- check the package version numbers in package.config and verify that the MVC4 template doesn't include packages that are too recent. See my post where I had to take a lower version of Entity Framework.
- compile against .NET framework 4.0 instead of 4.5

If you can get it to work I'd really appreciate if you could post (a link to) your solution

justin said...

Great post! However, I'm having a little problem. I get the following error when trying to access the the DataServiceController (WebAPI):

Method not found: 'Void System.Net.Http.Headers.HttpHeaders.AddWithoutValidation(System.String, System.Collections.Generic.IEnumerable`1)'.


I made the change to the Routing.config file, so it should be hitting the controller just fine. Here is the controller code

DataServiceController.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Http.Data.EntityFramework;
using myNamespace.Models;

namespace myNamespace.Controllers
{
public class DataServiceController : DbDataController
{
public IQueryable GetTags()
{
return DbContext.Tags.Where(t => t.approvedOn.HasValue && !t.flaggedOn.HasValue).OrderBy(t => t.name);
}
}
}


Any ideas? Thanks!!

Bart Jolling said...

Hi Justin,

I guess you are trying this in Visual Studio 2012? Is that correct?

My article only applies to Visual Studio 2010. In order to make it work I had to keep references to older version of many NuGet packages. The error you are seeing is because of trying to use an older version of 'System.Net.Http' against a version of MVC that is too recent.

Maybe you could get it to work by finding a newer version of this System.Net.Http from Nuget but that will probably just break other references.

I haven't tried on VS2012 due to lack of time but I'm pretty sure it will not be possible to re-create the 'Single Page Application' template using the MVC4 beta packages. You probably should use pure WebAPI services and another REST javascript library for the time being.

Hope this helps.

Anonymous said...

Thanks for the quick reply Bart. Unfortunately, I'm using VS2010. Do you know what version System.Net.Http I need to be using?

Bart Jolling said...

Have a look at my example project. That one works (on my machine) and contains the right versions: http://sdrv.ms/N6PCwv

See if it compiles and starts up. Then try to find out what is different in your application.

justin said...

Same error... do I need to update DLLs in my assembly? Or set references to all copy to local?

Bart Jolling said...

I'm afraid that I can't help you. I have followed my own advice and downloaded the example project from my skyDrive. Then I decompiled the System.Net.Http.dll that is include in the packages folder.

I have no trouble finding the "AddWithValidation" function.

/// <summary>
/// Adds the specified header and its values into the <see cref="T:System.Net.Http.Headers.HttpHeaders"/> collection without validating the provided information.
/// </summary>
/// <param name="name">The header to add to the collection.</param><param name="values">The values of the header.</param>
public void AddWithoutValidation(string name, IEnumerable<string> values)
{
if (values == null)
throw new ArgumentNullException("values");
this.CheckHeaderName(name);
HttpHeaders.HeaderStoreItemInfo createHeaderInfo = this.GetOrCreateHeaderInfo(name, false);
foreach (string str in values)
HttpHeaders.AddValue(createHeaderInfo, (object) (str ?? string.Empty), HttpHeaders.StoreLocation.Raw);
}

chrisl said...

Hi Bart,

Like others I can't get the code to work because I have installed the VS2012 RC (soon to be RTM) - I get the 'System.MissingMethodException: Method not found: 'Void System.Net.Http.Headers.HttpHeaders.AddWithoutValidation(System.String, System.Collections.Generic.IEnumerable`1)'.' error.

Do you anticipate updating this article to work with the new VS? I, for one, would love to see this.

SPA with MVC4 is incredibly interesting and compelling, but because of the current state of MVC, Upshot, etc. it is all a bit of a frustrating mess! Here's hoping Steve Sanderson et al get some time soon to get this finished - it will be a silver bullet solution for many people looking to target web+mobile!

Thanks

Jose Luis Serrano Fernandez said...

Hello

I have vs2010 with MVC4RC EF 4.3.1 and i follow your guides to install SPa using nuget.

well my question is if i use system.json upshot and dbcontrollers run well except for the curcular referecne issues on entities, if i change to newtownsoft.json formmater this goes well but i have problems with the dbcontrollers (sorting, Odata mainly)

Could we have some state of versions to have this thing running (best with newtonsoft.json)?

Thanks

Bart Jolling said...

Like some of the posters above I tried to migrate my DeliveryTracker projects to .NET 4.5 using VS2012 and got stuck on the same "method AddWithoutValidation not found".

I don't intend to invest much time anymore in SPA because of the uncertainty of what the final package will be. I still have 2 posts on the topic in draft but I don't think it's very useful to publish them if things are going to function entirely differently in the upcoming versions.

Same for the question on system.json vs newtonsoft.json. Currently the former works quite OK but in the future the latter will be used. But I don't know in what way this will break existing code written against the MVC4 Beta so I prefer to wait it out.

G said...

Hi Bart,

Keep up the good work!

One question for you, that I am struggling with my own SPA.

When the application is unable to submit (e.g. from mobile device with no signal) upshot handles caching of the data in the browser until it can submit. This is exactly what I need.

What I am struggling with is how to determine if this has happened, so that I can warn the user against navigating away from the site, and giving them an option to try again with the submit.

Do you know how this could be achieved?

Bart Jolling said...

In Steven Sanderson's demo he was using a "OfflineProvider" together with his datasources. This was created to solve exactly your problem but was never released. I think it's best to put this SPA libray aside for the moment and wait until an official release

G said...

Hi Bart,

Waiting was not an option.

I got around this by manually submitting to the server - the commitChanges method on the data source allows you to pass in 2 functions - one for success, one for failure, like so:

self.saveAll = function () {
self.dataSource.commitChanges(savedOK, didntSave);
}

Anonymous said...

Hi Bart, just looking at this tutorial, is it possible to get it running without sql express, i have sql server 2008. If i had sql express is the database build automatically when you download the source code??