Using AJAX in Microsoft MVC applications (with Entity Framework)
The article shows how to use AJAX in your applications based on the Microsoft MVC Framework.
Introduction
In this article, I will explain how to use AJAX in MVC applications. I will get data from the database using an Entity Framework. For partial web page updating, I use the fascinating JS framework prototype.
What Do you Need to Start
- Visual Studio 2008 (beta)
- MVC Framework
- ADO.NET Entity Framework
- VS 2008 and Microsoft .NET 3.5 SP1
- Framework Prototype
Task
I have two drop downs on my page: a list of countries and a list of cities. When I select a country in the first dropdown, the list of cities in the second one should be updated, of course, without reloading the whole page.
Plan
Although the task is trivial, I think it would be better to have some plan of developing.
- Database — tables for countries and cities
- Entity Framework — interface for accessing the data
- Model — getting the lists of countries and cities
- Controller — action methods for viewing the main page and transferring objects to views
- Views
- AJAX (prototype) — JavaScript code for loading data into the dropdowns
Solution
- Database — I have created two tables,
Country
andCity
.City
is connected withCountry
throughCountryCode
: - Entity Framework — We should generate code for accessing database objects like
Country
andCity
. To do that, right click on the Models folder, then click Add ..., and select ADO.NET Entity Data Model. Then, we select the tables from the list: Country, City. After we have clicked the Finish button, the studio generates the model and the classes. To update this model (after creating), you should go to the Model Browser (double click on the .edmx file), click on the root object, and select Update Model from the database. - Model — Let's create two classes — for countries and cities. The first class,
StaticData
, has a private method for getting the list of cities, and a public property:public class StaticData { public List<Country> CountryList { get { return GetAllCountries(); } } private static List<Country> GetAllCountries() { List<Country> countries = null; using (DataCoreConnection context = new DataCoreConnection()) { countries = (from c in context.Country select c).ToList(); } return countries; } }
The second class for cities is almost the same, but the constructor takes a parameter, the code for the country that the cities should be selected for:
public class Cities { private string countryCode; public Cities(string CountryCode) { countryCode = CountryCode; } public List<City> CitiesForCountry { get { return GetCitiesForCountry(countryCode); } } private List<City> GetCitiesForCountry(string CountryCode) { List<City> list = null; using (DataCoreConnection context = new DataCoreConnection()) { list = (from c in context.City where c.Country.Code == CountryCode select c).ToList(); } return list; } }
- Controller — We use the existing
HomeController
because our dropdowns are on the home page. We add only one action method for showing the view that gives the contents for the cities dropdown:public void UpdateCities(string code) { Cities cities = new Cities(code); RenderView("SelectCity", cities); }
Here, we get a list of cities (the object
Cities
) and pass it to theSelectCity
view. Don't forget to add a routing rule into Global.asax:routes.Add(new Route("Home/UpdateCities/{code}", new RouteValueDictionary(new { controller = "Home", action = "UpdateCities" }), new MvcRouteHandler()));
- Views — We use two views, one for the main page with static data, and one for the contents of the dropdowns with cities. Start from the existing view — Index.aspx. As we should pass the
StaticData
object to it, our view has to be inherited from not justViewPage
but also fromViewPage<StaticData>
:public partial class Index : ViewPage<StaticData> { }
Let's show the list of countries on the page itself:
<select id="selCountry" onchange="UpdateCitiesList();"> <option value="0" selected="selected"><Select a country></option> <% foreach (var country in ViewData.CountryList) { %> <option value='<%= country.Code %>'> <%= country.Name %> </option> <% } %> </select>
This code is very simple, so it seems to me there is no need to explain it. I just noticed that the
ViewData
object has theStaticData
type, so we can call its members without any casting. The handlerUpdateCitiesList()
that gets the list of cities will be discussed a little later. Now, let's create one more view for showing the cities:public partial class SelectCity : ViewPage<Cities> { }
And, add the code generating the contents of the dropdown:
<option value="0"><Select a city></option> <% foreach (var city in ViewData.CitiesForCountry) { %> <option value='<%=city.Id %>'><%= city.Name %></option> <%} %>
Firstly, we should add a link to the prototype file (I did so on the master page):
<script type="text/javascript" src="<%= Page.ResolveClientUrl("~/Views/Scripts/prototype_p.js") %>"></script>
Now, we add some code that will get data from the database without reloading the whole page:
<script type="text/javascript" language="javascript"> function UpdateCitiesList() { if ($('selCountry').value == "0") return; var url = '/Home/UpdateCities/' + $('selCountry').value; new Ajax.Updater('selCities', url, { method: 'get' } ); } </script>
This code is also very simple. First, we define what element of the dropdown is selected. If it is the "0" element (that only contains the text "Select ..."), we go out from the function. Else we create a URL to call the controller
Home
and theUpdateCities
action method (with the code of the selected country) and call the prototype library objectAjax.Updater
. This object allows us to get data in an asynchronous manner, i.e., without reloading the whole page.As soon as we select some country, the cities dropdown is updated.
Load and open the solution attached to this article. You may see an empty page, Default.aspx (bug in SP1), or maybe not. Anyway, you need to use this address: localhost:NNN/Home/ to see the result.
Latest Updates
Microsoft launched the 3 version of MVC Prerelease, so you can load it here. Don't forget to load the Readme file (at the bottom of loading page) where you will find the full list of changes. I was trying to make over my project how the Readme advised, and I got the absolutely-compiled-but-absolutely-non-working code (the routing doesn't work so far). So I have created a new project (where the routing works for some reason) and transfered all files there. So you can find a new version of project (for ASP.NET Prerelease 3) at the top of article.
You can find a version of this article in Russian in my blog.
Comments and suggestions are welcome!