May
15
2012
MVC4 // SPA

Building a Single Page Application without Entity Framework - Part I

A new feature in MVC 4 Beta is the ASP.NET Single Page Application.

SPA provides a very nice way of delivering high-end websites with amazing client-side interaction.

On Steven Sanderson's blog there is a great post about SPA with a very nice DeliveryTracker sample and on the ASP.NET SPA website there are several good walkthroughs and samples explaining how to build common scenarios with SPA.

These samples, however, all show how you can build a SPA site with Entity Framework as the database back-end.

This post will explain how you can use SPA (or upshot to be specific) without Entity Framework.

 

A part of SPA is the upshot JavaScript library which provides data access and caching. Upshot uses the new WebAPI for retrieving and saving the data.

The UpshotContext HtmlHelper is provided, which generates the necessary JavaScript that upshot needs to be able to use the WebAPI datasource you specified.

Below shows the UpshotContext as it is used in the Home view in the DeliveryTracker sample from Steven Sanderson.

Html.UpshotContext(bufferChanges: true).DataSource<DataServiceController>(x => x.GetDeliveriesForToday())

 

Suppose we have a simple Customer POCO.

public class Customer
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

If we want upshot to retrieve a list of Customers, we need to add a WebAPI Controller to our application and use this in the UpshotContext in our view.

The UpshotContext expects a TDataController where TDatacontroller is of type System.Web.Http.Data.DataController, which derives from ApiController.

In the existing samples i found, this TDataController is always of type System.Web.Http.Data.EntityFramework.DbDataController, which means it is using EF.

 

When we implement the new controller and derive it from DataController, we are not using any EF specific classes, so we're not using EF at all.

namespace MvcApplication27.Controllers
{
    public class CustomerServiceController : DataController
    {
        public IEnumerable<Customer> GetCustomers()
        {
            List<Customer> result = new List<Customer>();
            result.Add(new Customer() { Id = result.Count + 1, FirstName = "John", LastName ="Doe" });
            result.Add(new Customer() { Id = result.Count + 1, FirstName = "Jane", LastName = "Doe" });
            return result;
        }
    }
}

Off course in a real-world application the list of Customers will not be recreated each time in the GetCustomers method, but will be retrieved from some kind of storage/database.

 

At this time we can add the UpshotContext to our view.

Html.UpshotContext().DataSource<MvcApplication27.Controllers.CustomerServiceController>(x => x.GetCustomers())

But now, when we run our application, the UpshotContext will give us this error : "queryOperation 'GetCustomers' must return an entity type or an IEnumerable/IQueryable of an entity type" Cry

 

After digging through the MVC 4 Beta sourcecode (available here) i found out that this error is occuring because upshot doesn't know what the 'identity' property of Customer is.

Thankfully we can tell upshot what property this is by using the System.ComponentModel.DataAnnotations.KeyAttribute in our Customer POCO.

public class Customer
{
    [Key]
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

 

After this we can add a small ViewModel to our project.

function CustomersViewModel() {
    var self = this;
    self.dataSource = upshot.dataSources.Customers.refresh();
    self.customers = self.dataSource.getEntities();
}

 

And add a bit of Knockout.js to our view and we're done

@(Html.UpshotContext().DataSource<MvcApplication27.Controllers.CustomerServiceController>(x => x.GetCustomers()))

<h2>Customers</h2>
<ul data-bind="foreach: customers">
    <li>
        <div>Name: <em data-bind="text: FirstName"></em> <strong data-bind="text: LastName"></strong></div>
    </li>
</ul>

<script src="~/Scripts/CustomersViewModel.js" type="text/javascript"></script>
<script type="text/javascript">
    $(function () {
        ko.applyBindings(new CustomersViewModel());
    });
</script>

 

The next post will explain what needs to be done to have upshot understand relationships between POCO's and how to handle CRUD operations.

It will continue were this post ends and will add Order, Product and OrderProduct POCO's so we have one-to-many and many-to-many relationships.

Comments (6) -

Mike

Great post Marco.

If I understand it correctly, when using EF, the Wizard either maps the primary key from the corresponding table or attempts to infer the key based on the columns of the table or view.  Since we're not using EF, we must decorate the POCO with the [Key] Attribute.

I read somewhere that UpShot will also handle validation.  Have you seen posts which describe how UpShot/Knockout will work with validation?

Marco van Kimmenade

Hi Mike,

You understand completly! The Wizard does this for us.

When using EF Code First, Code First also automatically detects the primary key by using naming conventions. An Id property on your class will be used as the primary key. Because of the naming convention, when you have a Customer class and don't have an Id property but you named it CustomerId, EF Code First will understand that as well.
Same for CustomerNo but i'm not 100% sure for that one.

You are correct that Upshot/Knockout will also handle validation.
I just blogged the 2nd part of the "SPA without EF" series. In that post i also described how validation works.

If you have any questions please feel free to ask.

Regards,
Marco

Pavan

Thanks alot. I hit the same roadblock today, your blog saved me precious time

Leon van Bokhorst

Nice detailed explanation Marco. It makes me wanna jump on the SPA bandwagon. I hope to read a lot more from you.

SimpleScripts

Hi,great  post. Infos are very usefull and saves me huge amount of time which I spend on something else instead of searching posts like this Thank you

Oran

Hi Marco,

Thanks for the post. I have one further question.

I'm currently designing a new application using EF but I have an extra abstraction layer above the EF code (to cater for more complex database scenarios other than simple CRUD) which means that the layer exposed to my web api isn't a DataController but just a normal class library.

Is there any way to get upshot work in this case or do I have to get rid of this extra layer of abstraction?

Thanks
Oran

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

About me

I'm Marco, Developer to the bone living in The Netherlands..

The name Blue Basher is coming from the fact that i break a keyboard at least once a year.. Smile