Lab - Download Now DOC by HC111111002325

VIEWS: 20 PAGES: 73

									Hands-On Lab
Creating PlanMyNight ASP.NET MVC
Application
Lab version:   1.0.0

Last updated: 11/10/2011




                           Page 1
CONTENTS




OVERVIEW ................................................................................................................................................... 3

EXERCISE 1: CREATING AN ASP.NET MVC APPLICATION, PLANMYNIGHT ...................................... 4
       Task 1 – Creating PlanMyNight ASP.NET MVC Web Application Project.............................................. 5
       Task 2 – Creating PlanMyNight Model.................................................................................................. 6
       Task 3 – Creating PlanMyNight Views................................................................................................. 15
       Task 4 – Creating PlanMyNight Controllers ........................................................................................ 26
       Task 5 – Enhancing Views by Adding CSS and Images ........................................................................ 32
   Exercise 1: Verification                                                                                                                               33

EXERCISE 2: CREATING ENTITY FRAMEWORK DATA MODEL.......................................................... 38
       Task 1 – Creating an Entity Framework Data Model .......................................................................... 38
       Task 2 – Creating the ActivitiesRepository ......................................................................................... 41
       Task 3 – Updating Controllers to use Activities Repository ................................................................ 44
   Exercise 2: Verification                                                                                                                               45

EXERCISE 3: ADDING AJAX FOR SEARCHING ACTIVITIES................................................................ 51
       Task 1 – Adding ASP.NET 4.0 AJAX Preview 5 ..................................................................................... 51
       Task 2 – Creating ClientTemplatesSearchResults View ...................................................................... 52
       Task 3 – Creating Search Scripts ......................................................................................................... 56
       Task 4 – Creating AJAX Client Script Manager .................................................................................... 64
       Task5 – Modifying HomeController to use AJAX ................................................................................ 68
   Exercise 3: Verification                                                                                                                               69

SUMMARY .................................................................................................................................................. 72




                                                                          Page 2
Overview
This Hands-On Lab is based on the Plan My Night demo and uses the following technologies: MVC 2
Framework, Visual Studio 2010, .Net Framework 4.0 and ASP.NET AJAX.
Throughout the lab you will learn the simplicity, yet powerness of using all these technologies together.
You will start with a simple application and will build it until you have a fully funtional MVC Web
Application.
This Hands-On Lab assumes that the developer has basic experience with HTML, JavaScript, the ASP.NET
MVC Framework, and the Entity Framework. Within this training kit you will find labs which will work as an
introduction to those technologies.



Objectives
In this Hands-On Lab, you will learn how to:
       Create an ASP.NET MVC application from scratch based on PlanMyNight Demo
       Create an Entity Framework Data Model from an existing database

       Use Entity Framework as a repository for the MVC application
       Add unobtrusive AJAX capabilities to the MVC Application



System Requirements
You must have the following items to complete this lab:
       Microsoft Visual Studio 2010
       Microsoft SQL 2005 or Microsoft SQL 2008 (Express edition or above)

       ASP.NET 4.0 AJAX Preview 5 (on this Hands-On Lab you will find instructions on how to
        download and install it)



Setup
All the requisites for this lab are verified using the Configuration Wizard. To make sure that everything is
correctly configured, follow these steps.

 Note: To perform the setup steps you need to run the scripts in a command window with
 administrator privileges.



                                                  Page 3
       1. Run the Configuration Wizard for the Training Kit if you have not done it previously. To do
          this, run the CheckDependencies.cmd script located under the
          %TrainingKitInstallationFolder%\Labs\AspNetMvcPlanMyNight\Setup folder. Install any
          pre-requisites that are missing (rescanning if necessary) and complete the wizard.

          Note: For convenience, much of the code you will be managing along this lab is available as
          Visual Studio code snippets. The CheckDependencies.cmd file launches the Visual Studio
          installer file that installs the code snippets.




Exercises
This Hands-On Lab is comprised by the following exercises:
       1. Exercise 1: Creating an ASP.NET MVC Application, PlanMyNight
       2. Exercise 2: Creating Entity Framework Data Model
       3. Exercise 3: Adding AJAX for Searching Activities

Estimated time to complete this lab: 90 minutes.

 Note: Each exercise is accompanied by an End folder containing the resulting solution you should
 obtain after completing the exercises. You can use this solution as a guide if you need additional help
 working through the exercises.




Next Step
Exercise 1: Creating an ASP.NET MVC Application, PlanMyNight



Exercise 1: Creating an ASP.NET MVC
Application, PlanMyNight
In this exercise you will learn how to create an ASP.NET MVC application in Visual Studio 2010.
As a first step, we will use a stub data repository, which will be replaced, in exercise 2, with another one
that uses Entity Framework as data provider.




                                                   Page 4
You will also learn how to add paging, filtering, and sorting functionality to an existing ASP.NET MVC
application, as well as adding simple validation using Data Annotation Validators.
Task 1 – Creating PlanMyNight ASP.NET MVC Web Application Project
In this task you will create and configure an empty ASP.NET MVC application project using the MVC
Visual Studio template.
       1. Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio 2010
          | Microsoft Visual Studio 2010.
       2. On the File menu, point to New, and click Project.
       3. In the New Project dialog box make sure that .NET Framework 4 is selected, and select the
          Visual C# | ASP.NET MVC 2 Web Application project type. You may set the location to the
          %TrainingKitInstallFolder%\Labs\AspNetMvcPlanMyNight\Source\Ex01-
          CreatingPlanMyNight\begin folder.
       4. Change the Name to PlanMyNight, and click OK.




        Figure 1
        Create New Project Dialog Box




                                                 Page 5
       5. After selecting the OK button, you will be prompted to create a test project. Select No,
          because you will not create test cases on this lab.

         Note: When you create a new MVC Web application, Visual Studio gives you the option to
         create two projects at the same time. The first project is a Web project where you can
         implement your application. The second project is a testing project where you can write unit
         tests for your MVC components.
         It is a good practice to create both projects and test the whole application. On this lab we will
         avoid testing for simplicity.



       6. Configure the web site to use port 50000.
               a. To do this, in Solution Explorer, right-click the PlanMyNight project and in the
                  context menu select Properties.
               b. In the Property page open the Web tab.
               c. In the Servers section, select Specific Port.
               d. Set the port number to 50000.
               e. Press Ctrl + S to save changes.




                Figure 2
                Specifying a port number



Task 2 – Creating PlanMyNight Model
In this task, you will create the PlanMyNight Entities and Data Model. Also, you will see how paging,
validation, filtering and sorting functionalities are implemented in the PlanMyNight application.
       1. In Solution Explorer, right-click the Models folder, and add a new one named Entities


                                                 Page 6
2. Create a class named Activity, which will be the main entity of your application.
    a. Right-click the Entities folder.
    b. Point to Add and click Class.
    c. Type Activity.cs as the class Name and click Add.
    d. Replace the default implementation of the class with the following code:
        (Code Snippet – PlanMyNight MVC App – Activity Class)
        C#
        namespace PlanMyNight.Models.Entities
        {
            public partial class Activity
            {
                public string Id { get; set; }

                    public string PhoneNumber { get; set; }

                    public string Name { get; set; }

                    public string State { get; set; }

                    public string Zip { get; set; }

                    public string Street { get; set; }

                    public string City { get; set; }

                    public int ActivityTypeId { get; set; }

                    public int RatingCount { get; set; }

                    public double? Rating { get; set; }
               }
        }



            Note: Activities will be the main entity in your application.
            A user will perform a search providing certain search criteria for the activities
            repository, which you will create later on this exercise, in order to filter and return the
            group of activities which matches it



Activities will be mapped to a certain type (ActivityTypeId.) To do this, you will use a class that
wraps the activity-type related information.



                                             Page 7
3.   Create a class named ActivityType.
     a. In the Solution Explorer, right-click the Entities folder.
     b. Point to Add and click Class.
     c. Type ActivityType.cs as the class name, and click Add.
     d. Replace the default implementation of the class with the following code:
         (Code Snippet – PlanMyNight MVC App – ActivityType Class)
         C#
         namespace PlanMyNight.Models.Entities
         {
             public partial class ActivityType
             {
                 public int Id { get; set; }

                   public string Name { get; set; }

                   public string PluralizedName { get; set; }
              }
         }



4. Create an enumerator named SortCriteria, which will be used by the activities repository to
   determine how to sort the results.
     a. In the Solution Explorer, right-click the Models folder.
     b. Point to Add and click Class.
     c. Type SortCriteria.cs as the enumerator name, and click Add.
     d. Replace the default implementation of the class with the following code:
         (Code Snippet – PlanMyNight MVC App – SortCriteria Enumerator)
         C#
         namespace PlanMyNight.Models
         {
             public enum SortCriteria
             {
                 Name = 0,
                 ActivityType = 1,
                 Rating = 2
             }
         }




                                            Page 8
            Note: Even though we define only three sorting values, by using an enumerator to
            identify the sort criteria you are providing an extensibility point for your application.
            You could easily add new sorting criteria without much effort later if needed.



To manage the search criteria provided by the user, you will create the ActivitySearchCriteria
class to wrap all the filtering parameters.
5. Create a class named ActivitySearchCriteria.
    a. In the Solution Explorer, right-click the Entities folder.
    b. Point to Add and click Class.
    c. Type ActivitySearchCriteria.cs as the class name, and click Add.
    d. Replace the default implementation of the class with the following code:
        (Code Snippet – PlanMyNight MVC App – ActivitySearchCriteria Class)
        C#
        namespace PlanMyNight.Models.Entities
        {
            public class ActivitySearchCriteria
            {
                public int? ActivityTypeId { get; set; }

                    public string StreetAddress { get; set; }

                    public string City { get; set; }

                    public string State { get; set; }

                    public string Zip { get; set; }

                    public SortCriteria SortBy { get; set; }

                    public int Page { get; set; }

                    public int PageSize { get; set; }
               }
        }



            Note: ActivitySearchCriteria contains a property of type SortCriteria. This is how the
            activities repository will know how to sort the results.




                                             Page 9
Because the ActivitySearchCriteria class will have information provided by the user, it is a good
practice to add validation to it. To do this, you will use a Custom Validator, using Data
Annotation Validators.

  Note: for more information on using Data Annotation Validators on an ASP.NET MVC
  application you can visit: http://www.asp.net/learn/mvc/tutorial-39-cs.aspx



6. Open the ActivitySearchCriteria.cs file(if not already opened) and replace the class signature
   with the following:
(Code Snippet – PlanMyNight MVC App – ActivitySearchCriteria Class Header)
C#
     using System.ComponentModel.DataAnnotations;

     [CustomValidation(typeof(ActivitySearchCriteria), "IsValid")]
     public class ActivitySearchCriteria



  Note: The CustomValidation attribute allows you to define a custom validation method for the
  entity. In this case, the method will be named IsValid.



7. Add the IsValid method to ActivitySearchCriteria.cs by copying the following code bellow
   the PageSize property.
(Code Snippet – PlanMyNight MVC App – ActivitySearchCriteria IsValid Method)
C#
        public static ValidationResult IsValid(ActivitySearchCriteria
criteria)
        {
             if (!string.IsNullOrEmpty(criteria.City) ||
                  !string.IsNullOrEmpty(criteria.State) ||
                  !string.IsNullOrEmpty(criteria.Zip) ||
                  !string.IsNullOrEmpty(criteria.StreetAddress))
             {
                  return ValidationResult.Success;
             }
             else
             {
                  return new ValidationResult("Please provide a search
criteria.");
             }
        }




                                         Page 10
 Note: The custom validation method must return a ValidationResult.
 If no errors are found, ValidationResult.Success is returned; otherwise, a new instance of
 ValidationResult with the error message provided through the constructor method is
 returned.



To implement paging functionality in the model, you will create a wrapper of the list of activities
returned from the activities repository. This wrapper adds paging-related information to it.
8. Create a class named PagingResult.
    a. In the Solution Explorer, right-click the Entities folder.
    b. Point to Add and click Class.
    c. Type PagingResult.cs as the class name, and Add.
    d. Replace the default implementation of the class with the following code:
        (Code Snippet – PlanMyNight MVC App – PagingResult Class)
        C#
        namespace PlanMyNight.Models.Entities
        {
            using System;
            using System.Collections.Generic;

             public class PagingResult<T>
             {
                 public PagingResult(IEnumerable<T> items)
                 {
                     this.Items = new List<T>(items);
                     this.ItemType = typeof(T).ToString();
                 }

                  public int PageSize { get; set; }

                  public int TotalItems { get; set; }

                  public int CurrentPage { get; set; }

                public int TotalPages
                {
                    get
                    {
                        return (int)Math.Ceiling((decimal)this.TotalItems /
        this.PageSize);
                    }
                }



                                          Page 11
                    public ICollection<T> Items { get; private set; }

                    public string ItemType { get; private set; }
               }
        }



            Note: PagingResult will be used by the HomeController, to page back and forward
            within the results by sending paging-related information to the activities repository
            when invoking the search method.



The next step is to create the repository Interface. This interface will be implemented by the
stub repository on this exercise, and by the Entity Framework activities repository on exercise 2.
9. Create an interface named IActivitiesRepository. To do this, perform the following steps:
    a. In the Solution Explorer, right-click the Models folder
    b. Point to Add and click New Item.
    c. Select Interface under Visual C#.
    d. Type IActivitiesRespository.cs as the name, and click Add.
    e. Replace the default implementation of the interface with the following code:
        (Code Snippet – PlanMyNight MVC App – IActivitiesRepository Interface)
        C#
        namespace PlanMyNight.Models
        {
            using System.Collections.Generic;
            using Entities;

               public interface IActivitiesRepository
               {
                   Activity Retrieve(string id);

                    PagingResult<Activity> Search(ActivitySearchCriteria criteria);

                    IEnumerable<ActivityType> RetrieveActivityTypes();

                    IEnumerable<string> RetrieveStates();

                    void RateActivity(string activityId, byte rating);
               }
        }




                                           Page 12
          Note: The IActivitiesRespository interface has 5 methods:
          - Retrieve: This method returns the activity that matches the provided id. It is used for
          retrieving the information of a single activity.
          - Search: This method returns a PagingResult instance, which contains the filtered set
          of activities that matches the search criteria (which includes sorting and paging
          information.)
          - RetrieveActivityTypes and RetrieveStates: These methods are used for retreiving
          meta-information from the activities stored in the repository. This information will be
          used for injecting data into the search form, and populate the fields for the user to
          choose.
          - RateActivity: This method allows to rate a given activity. It is the only method that
          modifies the data model.



Once all entities are created you will proceed by adding the stub implementation of
IActivitiesRespository interface. To keep the exercise simple, the Hands-On lab provides this
implementation in the Assets folder.
10. Add the ActivitiesStubRepository.cs class implementation from the Assets folder. To do this,
    proceed as follows.
    a. In the Solution Explorer, right-click the Models folder.
    b. Point to Add and click Existing Item.
    c. Browse to the
       %TrainingKitInstallFolder%\Labs\AspNetMvcPlanMyNight\Source\Assets\Models
       folder.
    d. Select the ActivitiesStubRespository.cs and SearchHelper.cs classes and click Add.




                                         Page 13
        Figure 3
        Adding ActivitiesStubRepository Implementation



         Note: The ActivitiesStubRepository class implements the IActivityRepository
         interface, using two lists to simulate the database tables. As this repository is created
         to test the site’s functionality, the data is hardcoded into the constructor method.



11. Press CTRL+SHIFT+B to build the solution.
At this point, you have completed the application model. Your solution should look like the
following:




                                         Page 14
        Figure 4
        Solution Explorer Models Structure




Task 3 – Creating PlanMyNight Views
In this task you will modify the existing views and create new ones to show information to the user, and
get its input for searching and rating activities.
       1. In the Solution Explorer, double click the Site.Master file, located at Views\Shared, to open
          it




                                                Page 15
Figure 5
Site.Master in Solution Explorer


2. Replace the stylesheet definition, adding the following bolded code inside the header tag of
   Site.Master.
ASP.NET
<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=8" />
    <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
    <asp:ContentPlaceHolder ID="HtmlHeadContent" runat="server" />

</head>



  Note: In the meta-tag, the character encoding of the site is defined as UTF-8.
  The HtmlHeadContent asp:ContentPlaceHolder will be used to define the keywords meta-tag
  with search-related information. This meta-tag is used by crawler based search engines when
  indexing a website.



3. Replace Site.Master’s body definition with the following bolded code:
(Code Snippet – PlanMyNight MVC App – SiteMaster BodyDefinition)
ASP.NET
<body>

     <div id="container">



                                         Page 16
          <div id="header">
              <div id="logo">
                  <h1><a href="<%=Url.Content("~/")%>">Plan My Night</a></h1>
              </div>

              <hr />
              <div id="navigation">
                  <ul>
                       <li><%=Html.ActionLink("Search", "Index", "Home")%></li>
                       <li><a href="#">About</a></li>
                  </ul>
              </div>
          </div>

         <div id="pageWrapper">
             <div id="page">
                 <hr />
                 <div id="main">
                     <asp:ContentPlaceHolder ID="MainContent" runat="server" />
                 </div>
             </div>
         </div>
         <br />
     </div>

</body>



  Note: The code you have just added has two main sections.
  In the first one, the header, you defined the logo and the navigation bar. Notice that the
  navigation bar has an Html.ActionLink, which invokes the HomeController’s Index method,
  and displays “search” on the page.
  In the second section, the pageWrapper, you defined the ContentPlaceHolder for the
  MainContent. The changes done to this second section are for styling purposes only, and they
  will be added later in this exercise.



The Index.aspx page will define the content of the three ContentPlaceHolders of Site.Master.
4. In the Solution Explorer, double-click the Index.aspx file, located in the Views/Home folder,
   to open it.




                                        Page 17
Figure 6
Index.aspx in Solution Explorer


5. Add the definition of the HtmlHeadContent ContentPlaceholder. To do this, paste the
   bolded code before the TitleContent content definition.
ASP.NET
<asp:Content runat="server" ContentPlaceHolderID="HtmlHeadContent">
    <% if(!String.IsNullOrEmpty(ViewData["KeywordsMetatag"] as string)) { %>
        <meta name="keywords"
content="<%=Html.AttributeEncode(ViewData["KeywordsMetatag"].ToString())%>" />
    <% } %>
</asp:Content>

<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent"
runat="server">



  Note: The if statement checks if the KeywordsMetatag is provided, if so, it defines the meta-
  tag named keywords, and set its content. As explained before, this tag is used by crawler-
  based search engines to index your web-site.



6. Replace the TitleContent content definition with the following code to provide different
   PageTitle depending on the used search criteria.
ASP.NET
<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent"
runat="server">

    Plan My Night - <%= ViewData.ContainsKey("CriteriaDescription") ?
ViewData["CriteriaDescription"].ToString() : "Search Activities"%>



                                        Page 18
</asp:Content>



To provide the MainContent content, you will use two MVC View User Controls, SearchForm
and ActivitiesSearchResults. To render these controls, you will use the Html.RenderPartial
helper method.
7. Replace the MainContent content definition with the bolded code that follows.
ASP.NET
<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent"
runat="server">

<% Html.RenderPartial("SearchForm"); %>
<hr />
<% Html.RenderPartial("ActivitiesSearchResults"); %>

</asp:Content>



  Note: SearchForm is the view used to capture search parameters input, while
  ActivitiesSearchResults will be used to render the search result.



8. Create ActivitiesSearchResults MVC 2 View User Control. To do this, proceed as follows.
       a. In the solution explorer, right-click the Home folder, located inside Views
       b. Point to Add and click New Item.
       c. Select MVC 2 View User Control, under Visual C#\Wev\MVC.
       d. Type ActivitiesSearchResults.ascx as the user control name, and click Add.
9. Open the ActivitiesSearchResult.ascx file, if not already opened, and add the bolded code
   that follows:
(Code Snippet – PlanMyNight MVC App – ActivitiesSearchResult SearchResultDiv)
ASP.NET
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

<div id="searchResultsStatic" class="panel searchResults">
    <% var model = ViewData["PagingResult"] as PagingResult<Activity>; %>
    <% var searchCriteria = ViewData["SearchCriteria"] as
ActivitySearchCriteria; %>
    <div class="innerPanel">
        <h2>
             <ul>
                  <li><strong>Activities</strong></li>


                                        Page 19
              </ul>
          </h2>

          <div>


        </div>
    </div>
</div>



  NOTE: In the code above, you have defined two variables, model and searchCriteria. These
  variables were provided through the ViewData dictionary and will store the search results
  (model) and the search criteria (searchCriteria.)
  This information will be provided by the HomeController and used to render the results by the
  Views.



10. To provide the end-user with sorting functionality Add the sorting controls to
    ActivitiesSearchResult user control. To do this, proceed as follows.
    a. Insert the bolded code that follows inside the <div> tag at the bottom of
       ActivitiesSearchResult user control:
        (Code Snippet – PlanMyNight MVC App – ActivitiesSearchResult SortingControls)
        ASP.NET
                  <div>

                    <% if(searchCriteria != null) { %>
                    <div class="subheader">
                        Sort by:
                        <% if(searchCriteria.SortBy == SortCriteria.Name) { %>
                            <strong>Name</strong>
                        <% } else { %>
                            <%=Html.ActionLink("Name", "Search", new
        ActivitySearchCriteria { SortBy = SortCriteria.Name, State =
        searchCriteria.State, City = searchCriteria.City, StreetAddress =
        searchCriteria.StreetAddress, Zip = searchCriteria.Zip, ActivityTypeId =
        searchCriteria.ActivityTypeId })%>
                        <% } %>
                        |
                        <% if(searchCriteria.SortBy == SortCriteria.Rating) { %>
                            <strong>Rating</strong>
                        <% } else { %>
                            <%=Html.ActionLink("Rating", "Search", new
        ActivitySearchCriteria { SortBy = SortCriteria.Rating, State =



                                         Page 20
        searchCriteria.State, City = searchCriteria.City, StreetAddress =
        searchCriteria.StreetAddress, Zip = searchCriteria.Zip, ActivityTypeId =
        searchCriteria.ActivityTypeId })%>
                        <% } %>
                        |
                        <% if(searchCriteria.SortBy ==
        SortCriteria.ActivityType) { %>
                            <strong>Type</strong>
                        <% } else { %>
                            <%=Html.ActionLink("Type", "Search", new
        ActivitySearchCriteria { SortBy = SortCriteria.ActivityType, State =
        searchCriteria.State, City = searchCriteria.City, StreetAddress =
        searchCriteria.StreetAddress, Zip = searchCriteria.Zip, ActivityTypeId =
        searchCriteria.ActivityTypeId })%>
                        <% } %>
                    </div>
                    <% } %>

                  </div>



  Note: The code above renders the different sorting options (Rating, Type and Name.)
  If the items are sorted using the sorting criteria which is being rendered, the view will render
  the sort option in a <strong> tag; otherwise it will render an ActionLink.
  The ActionLink invokes the HomeController’s Search method, maintaining the search criteria
  but with the updated sorting option.



11. To render the search results into the view, add the bolded code that follows after the sorting
    controls in ActivitiesSearchResult.ascx.
(Code Snippet – PlanMyNight MVC App – ActivitiesSearchResult SearchResults)
ASP.NET
               </div>
               <% } %>
               <div class="items">
                   <% if(model == null) %>
                   <% { %>
                       <h3>Please provide a search criteria...</h3>
                   <% } else { %>
                       <% if(model.TotalItems == 0) %>
                       <% { %>
                           <h3>No activities found...</h3>
                       <% } else {%>
                           <ul class="activities">
                               <% foreach(var activity in model.Items) %>


                                          Page 21
                            <% { %>
                            <li>
                               <%
                                    var rating = activity.Rating ?? 0;
                                    rating = Math.Round(rating * 2) / 2;
                                 %>
                                 <span class="off
id"><%=Html.Encode(activity.Id)%></span>
                                 <h3>
                                 <% if (rating > 0)
                                    { %>
                                      <span class="rating
rating_<%=rating.ToString("0.0").Replace(".", "_")%>"><span>Rating:
</span><%=rating.ToString("0.0")%></span>
                                 <%} %>
                                      <%=Html.ActionLink(activity.Name,
"Details", "Activities", new { id = activity.Id }, null)%>
                                 </h3>
                                 <p><%=Html.Encode(activity.Street)%> |
<%=Html.Encode(activity.City)%>, <%=Html.Encode(activity.State)%>
<%=Html.Encode(activity.Zip)%> | Phone:
<%=Html.Encode(activity.PhoneNumber)%></p>
                            </li>
                            <% } %>
                        </ul>
                    <% } %>
                <% } %>
            </div>

          </div>



  Note: The code above provides three different render options.
  1. - If the model is null, it means that no search was done. In that case, it will render a message
  asking the user to provide search criteria.
  2. - If the model has no items, it means that no activities matching the search criteria were
  found. In that case, it will render a message informing the user that no activities were found.
  3. - Otherwise it will render the search results.
  Notice that the code has an ActionLink which invokes ActivitiesController’s Details method.
  This method searches for the details of a given activity and returns the Details view populated
  the information found.



12. To Complete the ActivitiesSearchResult user control, you will need to add the paging
    controls. To do so, copy the bolded code below after the code you have just added.


                                           Page 22
(Code Snippet – PlanMyNight MVC App – ActivitiesSearchResult PagingControls)
ASP.NET
        </div>

       <div class="toolbox">
            <% if (model != null && model.TotalPages > 0) %>
            <% { %>
                <div class="pager">
                    <% if(model.CurrentPage == 1) %>
                    <% { %>
                        <strong>«</strong>
                    <% } else { %>
                        <%=Html.ActionLink("«", "Search", new
ActivitySearchCriteria { Page = model.CurrentPage - 1, SortBy =
searchCriteria.SortBy, State = searchCriteria.State, City =
searchCriteria.City, StreetAddress = searchCriteria.StreetAddress, Zip =
searchCriteria.Zip, ActivityTypeId = searchCriteria.ActivityTypeId })%>
                    <% } %>
                    |
                    <% for(int i=1; i<=model.TotalPages; i++) %>
                    <% { %>
                        <% if(i == model.CurrentPage) { %>
                             <strong><%=i%></strong>
                        <% } else { %>
                             <%=Html.ActionLink(i.ToString(), "Search", new
ActivitySearchCriteria { Page = i, SortBy = searchCriteria.SortBy, State =
searchCriteria.State, City = searchCriteria.City, StreetAddress =
searchCriteria.StreetAddress, Zip = searchCriteria.Zip, ActivityTypeId =
searchCriteria.ActivityTypeId })%>
                        <% } %>
                        |
                    <% } %>

                    <% if(model.CurrentPage == model.TotalPages) %>
                    <% { %>
                        <strong>»</strong>
                    <% } else { %>
                        <%=Html.ActionLink("»", "Search", new
ActivitySearchCriteria { Page = model.CurrentPage + 1, SortBy =
searchCriteria.SortBy, State = searchCriteria.State, City =
searchCriteria.City, StreetAddress = searchCriteria.StreetAddress, Zip =
searchCriteria.Zip, ActivityTypeId = searchCriteria.ActivityTypeId })%>
                    <% } %>
                </div>
            <% } %>
        </div>

          </div>



                                      Page 23
  Note: The code above renders the paging controls. It possible action is rendered as an
  ActionLink that invokes the HomeController’s Search method, maintaining the search criteria
  but with the updated page number.



13. Add the SearchForm.ascx user control to the solution. This user control is provided as an
    Asset for this lab.
    a. In the Solution Explorer, right-click the Home folder, located under Views
    b. Point to Add and click Existing Item,
    c. Browse to the
       %TrainingKitInstallFolder%\Labs\AspNetMvcPlanMyNight\Source\Assets\Views\Hom
       e folder
    d. Select SearchFrom.ascx user control and click Add.




        Figure 7
        Adding SearchForm.ascx


To populate SearchForm.ascx’s fields, with the data on your IActivitiesRepository
implementation, you will need to provide two sets to it, the ActivityTypes, and States. These
values will be provided by the IActivitiesRepository’s RetreiveActivityTypes and RetrieveStates
methods.


                                        Page 24
14. Add a new class named ActivitiesSearchFields. To do so, proceed as follows.
    a. In the Solution Explorer, right-click the PlanMyNight project.
    b. Point to Add, and click New Folder.
    c. Change its name to ViewModels.
    d. Right-click the ViewModels folder.
    e. Point to Add, and click Class
    f.   Type ActivitiesSearchFields.cs as class Name, and click Add.
    g. Replace the default class implementation with the following code:
         (Code Snippet – PlanMyNight MVC App – ActivitySearchFields Class)
         C#
         namespace PlanMyNight.ViewModels
         {
             using System.Collections.Generic;
             using System.Web.Mvc;

              public class ActivitySearchFields
              {
                  public IEnumerable<SelectListItem> ActivityTypes { get; set; }

                   public IEnumerable<SelectListItem> States { get; set; }
              }
         }

15. Add the Details view, which will be used to render the details of an activity. This view is
    provided as an Asset for this lab.
    a. In the Solution Explorer, right-click the Views folder.
    b. Point to Add, and click New Folder.
    c. Change its name to Activities.
    d. Right-click the Activities folder.
    e. Point to Add, and click Existing Item
    f.   Browse to the
         %TrainingKitInstallFolder%\Labs\AspNetMvcPlanMyNight\Source\Assets\Views\Activ
         ities folder.
    g. Select Details.aspx view and click Add.
To make the view’s code more readable, when using types you avoided including the
namespace to it. For the view to find these types, you need to add the namespaces to the
Web.Config file.


                                            Page 25
       16. In the Solution Explorer, double-click the Web.Config file, located under PlanMyNight
           project, to open it.




        Figure 8
        Web.Config in Solution Explorer


       17. Scroll down to the <namespaces> tag, located inside <pages>, and add the following bolded
           code:
        XML
                <namespaces>
                  <add namespace="System.Web.Mvc"/>
                  <add namespace="System.Web.Mvc.Ajax"/>
                  <add namespace="System.Web.Mvc.Html"/>
                  <add namespace="System.Web.Routing"/>

                  <add namespace="PlanMyNight.Models" />
                  <add namespace="PlanMyNight.Models.Entities" />
                  <add namespace="PlanMyNight.Controllers" />
                  <add namespace="PlanMyNight.ViewModels"/>
                </namespaces>
              </pages>



       18. Press CTRL+SHIFT+B to build the solution.



Task 4 – Creating PlanMyNight Controllers
In this task you will modify HomeController, and add the ActivitiesController in order to handle user
interaction.


                                                Page 26
1. In the Solution Explorer, double-click HomeController.cs file, located under the Controllers
   folder to open it.
2. Add the following namespace directives to the file:
(Code Snippet – PlanMyNight MVC App – HomeController Using Directives)
C#
     using    System.Collections.ObjectModel;
     using    System.ComponentModel.DataAnnotations;
     using    System.Globalization;
     using    System.Text;
     using    PlanMyNight.Models;
     using    PlanMyNight.Models.Entities;
     using    PlanMyNight.ViewModels;



3. Add the following class variables and properties to HomeController:
(Code Snippet – PlanMyNight MVC App – HomeController VariablesAndProperties)
C#
          private const int DefaultPageSize = 5;

          private readonly IActivitiesRepository activitiesRepository;

          private IEnumerable<ActivityType> activityTypes;

        private IEnumerable<ActivityType> ActivityTypes
        {
            get
            {
                if (activityTypes == null)
                {
                    this.activityTypes =
this.activitiesRepository.RetrieveActivityTypes();
                }

                    return this.activityTypes;
                }
          }



4. Add a constructor method with an IActivitiesRepository as parameter, and set the
   activitiesRepository class property with the received values.
(Code Snippet – PlanMyNight MVC App – HomeController Constructors)
C#
          public HomeController() :


                                        Page 27
              this(new ActivitiesStubRepository())
         {
         }

         public HomeController(IActivitiesRepository activitiesRepository)
         {
             this.activitiesRepository = activitiesRepository;
         }



 Note: Notice that you have also added the default constructor which creates a new instance of
 ActivitiesStubRepository and invokes the added constructor with it as parameter. This is a
 good practice, since it will let you mock the repository when testing, and provides an
 extensibility point for your application.



5. In the HomeController, add a method named InjectActivitySearchFields, and add the
   following behavior to it.
(Code Snippet – PlanMyNight MVC App – HomeController InjectActivitySearchFields)
C#
        private void InjectActivitySearchFields(IDictionary<string, object>
viewData, ActivitySearchCriteria searchCriteria)
        {
            var types = this.ActivityTypes.Select(o => new SelectListItem {
Text = o.Name, Value = o.Id.ToString(), Selected = (searchCriteria != null &&
searchCriteria.ActivityTypeId.HasValue && o.Id ==
searchCriteria.ActivityTypeId.Value) }).ToList();
            types.Insert(0, new SelectListItem { Text = "Any activity", Value
= "0" });

            var states = this.activitiesRepository.RetrieveStates()
                                        .Select(o => new SelectListItem { Text
= o, Value = o, Selected = (searchCriteria != null && o ==
searchCriteria.State) }).ToList();
            states.Insert(0, new SelectListItem { Text = "Any state", Value =
string.Empty });

              viewData["SearchFields"] = new ActivitySearchFields
              {
                  ActivityTypes = types,
                  States = states,
              };
         }




                                       Page 28
 Note: InjectActivitySearchFields method creates an instance of ActivitySearchFields, it adds
 the ActivityTypes and States available in the activities repository and stores it in the ViewData
 dictionary for the view to consume.
 SearchForm user control uses the ActivitySearchFields to populate its controls with data.



6. In the HomeController, add a method named GetCriteriaDescription, which will parse the
   ActivitySearchCriteria into a string.
(Code Snippet – PlanMyNight MVC App – HomeController GetCriteriaDescription)
C#
private string GetCriteriaDescription(ActivitySearchCriteria searchCriteria,
string separator = " | ")
        {
            StringBuilder title = new StringBuilder();
            if (searchCriteria.ActivityTypeId > 0)
            {
                title.Append(this.ActivityTypes.Where(a => a.Id ==
searchCriteria.ActivityTypeId).Select(a =>
a.PluralizedName).FirstOrDefault());
                title.Append(separator);
            }

              if (!string.IsNullOrEmpty(searchCriteria.City))
              {
                  title.Append(searchCriteria.City);
                  title.Append(separator);
              }

              if (!string.IsNullOrEmpty(searchCriteria.State))
              {
                  title.Append(searchCriteria.State);
                  title.Append(separator);
              }

              if (!string.IsNullOrEmpty(searchCriteria.Zip))
              {
                  title.Append(searchCriteria.Zip);
                  title.Append(separator);
              }

            title.AppendFormat(CultureInfo.CurrentUICulture, "Page {0}",
searchCriteria.Page);

              return title.ToString();
         }



                                        Page 29
 Note: This is a helper method, and simply concatenates the information provided as search
 criteria, separated by the given separator (| by default.)



7. Replace HomeController’s Index method implementation to invoke
   InjectActivitySearchFields method before creating the View. To do so, copy the bolded code
   that follows into the body of the method.
C#
          public ActionResult Index()
          {
              this.InjectActivitySearchFields(this.ViewData, null);
              return View("Index");
          }



To complete the HomeController you will add the Search method. Among other tasks, this
method will use the CustomValidator you previously added to ActivitySearchCriteria entity to
validate if the search criteria provided is valid or not.
If the validation succeeds, it executes the actual search, pages the result and stores it in the
ViewData dictionary for the view to use.
8. Add the Search method to the HomeController.cs file:
(Code Snippet – PlanMyNight MVC App – HomeController SearchAction)
C#
public ActionResult Search(ActivitySearchCriteria searchCriteria)
        {
            if (searchCriteria.ActivityTypeId.HasValue &&
searchCriteria.ActivityTypeId == 0)
            {
                searchCriteria.ActivityTypeId = null;
            }

            // Validation
            var errors = new Collection<ValidationResult>();
            Validator.TryValidateObject(searchCriteria, new
ValidationContext(searchCriteria, null, null), errors);
            if (errors.Any())
            {
                ModelState.AddModelError("ActivitySearchCriteria", "Please
provide a search criteria.");
                return this.Index();
            }



                                          Page 30
               searchCriteria.PageSize = DefaultPageSize;
               if (searchCriteria.Page <= 0)
               {
                   searchCriteria.Page = 1;
               }

               // search activities
               var activities = this.activitiesRepository.Search(searchCriteria);
               this.ViewData["PagingResult"] = activities;

            this.ViewData["SearchCriteria"] = searchCriteria;
            this.InjectActivitySearchFields(this.ViewData, searchCriteria);
            this.ViewData["CriteriaDescription"] =
this.GetCriteriaDescription(searchCriteria, " | ");
            this.ViewData["KeywordsMetatag"] =
this.GetCriteriaDescription(searchCriteria, ", ");
            return View("Index");
        }



9. Add the ActivitiesController controller to your project. This controller is provided as an Asset
   for this lab.
    a. In the Solution Explorer, right-click the Controllers folder.
    b. Point to add, and click Existing Item.
    c. Browse to the
       %TrainingKitInstallFolder%\Labs\AspNetMvcPlanMyNight\Source\Assets\Controllers
       folder
    d. Select ActivitiesController.cs and click Add.




                                         Page 31
                Figure 9
                Adding ActivitiesController.cs



                 Note: The ActivitiesController handles the user actions to a certain activity. It provides
                 the rating functionality, and injects the details of an activity into the Details view.



       10. Press CTRL+SHIFT+B to build the solution.



Task 5 – Enhancing Views by Adding CSS and Images
In this task you will add CSS and images to enhance the views of your solution, both are provided as
Assets to keep the exercise simple.
       1. Delete Site.css file provided by default
            a. In the Solution Explorer, right-click Site.css file, located under Content
            b. Click Delete.
            c. When prompted, confirm the action by choosing OK.
       2. Add Site.css file provided as an Asset which contains PlanMyNight’s style sheet.
            a. In the Solution Explorer, right-click the Content folder.
            b. Point to Add, and click Existing Item.

                                                 Page 32
            c. Browse to the
               %TrainingKitInstallFolder%\Labs\AspNetMvcPlanMyNight\Source\Assets\Content
               folder.
            d. Select Site.css and click Add.
       3. Create the Images folder that will store all the images used in your application.
            a. In the Solution Explorer, right-click the Content folder.
            b. Point to Add, and click New Folder.
            c. Change its name to Images
       4. Add the images provided as Assets to the Images folder
            a. In the Solution Explorer, right-click the Images folder, located under Content.
            b. Point to Add, and click Existing Item.
            c. Browse to the
               %TrainingKitInstallFolder%\Labs\AspNetMvcPlanMyNight\Source\Assets\Content\Im
               ages folder.
            d. Select All images and click Add.
       5. Press CTRL+SHIFT+B to build the solution.




Next Steps
Exercise 1: Verification



Exercise 1: Verification
In this verification you will check that you have correctly performed all steps of the exercise by running
PlanMyNight ASP.NET MVC application, search for activities and rate them.
       1. Press CTRL + F5 to run the program without debugging.




                                                  Page 33
Figure 10
PlanMyNight HomePage


2. Select OH as state and click Search. The stub repository will return the activities which match
   the given search constrain.
3. Click on an Activity name, the Details view will appear, showing all the information related
   to the activity you have chosen, and allowing you to rate it.




                                         Page 34
Figure 11
Search Results


4. To rate an Activity, select the desired rating value and click the rate link.




                                           Page 35
Figure 12
Rating an Activity



  Note: The rate link is an ActionLink, and will invoke the ActivitiesController rate method with
  the selected rate as parameter.



5. Finally, go to the home page, and search for activities in OH again.




                                         Page 36
       Figure 13
       Rating shown in search result



         Note: Notice that the rating is persisted. Because the stub repository is an in-memory
         repository, changes will not persist; if ASP.Net development server is stopped, changes will be
         lost.



       6. Feel free to use the application and test the sorting, paging, validation and filtering
          functionality.



Next Step
Exercise 2: Creating Entity Framework Data Model




                                                 Page 37
Exercise 2: Creating Entity Framework
Data Model
In this exercise you will learn how to add an Entity Framework Data Model to an existing Application,
and create a repository to filter, page and sort items from that data model.


Task 1 – Creating an Entity Framework Data Model
In this task you will create an Entity Framework Data Model and replace the entities created on exercise
1 with the ones provided by it.
       1. Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio
          2010.

         Note: To attach the database to SQL, Visual Studio needs to be started with administrator
         privileges. To do so, right-click the link in the start menu and select Run as Administrator



       2. Open the solution file Begin.sln located in the folder
          %TrainingKitInstallFolder%\Labs\AspNetMvcPlanMyNight\Source\Ex02-
          CreatingEFDataModel\Begin, and build the solution. Alternatively, you can continue working
          with the solution obtained after completing the previous exercise.
       3. Remove stub entities so you can replace them with Entity Framework Data Model’s Entities
            a. In the Solution Explorer, select Activity.cs and ActivityType.cs; located under
               Model\Entities folder.
            b. Right-click one of the selected files and click Delete.
            c. When prompted, click OK to confirm deletion.




                                                 Page 38
         Figure 14
         Delete Entities in Solution Explorer


4. Create the Data Model
    a. In the Solution Explorer, right-click the Entities folder, located under Models.
    b. Point to Add, and click New Item.
    c. Select ADO.NET Entity Data Model, under Visual C#
    d. Type PlanMyNight.edmx as the data model Name, and click Add.

          Note: After choosing add a wizard pops-up, allowing you to configure the connection
          string, which Tables, Views and Stored Procedures to map.



    e. In the Configuration Wizard, select Generate from database and click Next
    f.   Click the New Connection button to create a new database connection.


                                         Page 39
        i.   Select Microsoft SQL Server Database File (SqlClient) as Data source
        ii. Click the Browse button to set the database file name to use.
        iii. Browse to the
             %TrainingKitInstallFolder%\Labs\AspNetMvcPlanMyNight\Source\Assets\App
             _Data folder
        iv. Select PlanMyNight.mdf.
        v. Click OK to save the connection.




             Figure 15
             New Connection Dialog


g. Leave all other settings by default and click Next.
h. You will be prompted if you want to copy the file inside your project directory and
   change the connection string accordingly. Click Yes to proceed.
i.   In the “Choose your database objects” dialog, expand the Tables Item.



                                     Page 40
           j.   Check the Activity and ActivityType tables.

                 Note: The tables you check in this step will be mapped as entities in your Data Model.



           k. Type PlanMyNight.Models.Entities as namespace and click Finish.

                 Note: for more information on Entity Framework you can visit:
                 http://msdn.microsoft.com/en-us/library/bb386876.aspx



       5. Press CTRL+SHIFT+B to build the solution.

         Note: If the compilation process show errors related to the Activity or ActivityType classes
         open the ADO.NET Entity Data Model file PlanMyNight.edmx and press CTRL+S to save the
         diagram and force the PlanMyNight.Designer.cs to be regenerated, thus creating the entities
         classes.




Task 2 – Creating the ActivitiesRepository
In this task you will create an ActivitiesRepository which uses an ADO.NET Entity Data Model as data
source. You will also add filtering, paging and sorting functionality to it.
       1. Add a new class named ActivitiesRepository.cs.

         Note: ActivitiesRepository will have the same functionality as ActivitiesStubRepository but
         instead of being an in-memory repository, it will use SQL (through Entity Framework) and be
         persistent.



           a. In the Solution Explorer, right-click over Models folder.
           b. Choose Add, and select Class.
           c. Name it ActivitiesRepository.cs. and click Add
           d. Replace the class header to implement IActivitiesRepository.
                C#
                public class ActivitiesRepository : IActivitiesRepository



           e. Add the following namespace directive to the file
                C#
                using PlanMyNight.Models.Entities;



                                                Page 41
After creating ActivitiesRepository you will have to implement IActivitiesRepository’s methods:
Retrieve, Search, RetrieveActivityTypes, RetrieveStates and RateActivity.
       2. Add a method named Retrieve which will return the Activity that matches a specified Id.
       (Code Snippet – PlanMyNight MVC App – ActivitiesRepository RetrieveMethod)
       C#
               public Activity Retrieve(string id)
               {
                   using (var context = new PlanMyNightEntities())
                   {
                       return context.Activities.Where(a => a.Id ==
       id).SingleOrDefault();
                   }
               }



         Note: Since in the stub repository you were using a collection to store the activities, the code
         for each method is really similar. The difference exists mainly in the source of the information.
         On the ActivitiesRepository you use the ADO.NET Entities DataModel context, while
         ActivitiesStubRepository uses a collection stored in memory.



       3. Add a method named RetrieveActivityTypes which will return all ActivityTypes in the
          database.
       (Code Snippet – PlanMyNight MVC App – ActivitiesRepository RetrieveActivityTypesMethod)
       C#
                 public IEnumerable<ActivityType> RetrieveActivityTypes()
                 {
                     using (var context = new PlanMyNightEntities())
                     {
                         return context.ActivityTypes.ToList();
                     }
                 }



       4. Add a method named RetrieveStates which will return all different States within the
          Activities.
       (Code Snippet – PlanMyNight MVC App – ActivitiesRepository RetrieveStatesMethod)
       C#
                 public IEnumerable<string> RetrieveStates()
                 {


                                                Page 42
            using (var context = new PlanMyNightEntities())
            {
                return context.Activities.Select(a =>
a.State).Distinct().ToList();
            }
        }



5. Add a method named RateActivity which will update an Activity’s rating.
(Code Snippet – PlanMyNight MVC App – ActivitiesRepository RateActivityMethod)
C#
        public void RateActivity(string activityId, byte rating)
        {
            using (var context = new PlanMyNightEntities())
            {
                var activity = context.Activities.Where(a => a.Id ==
activityId).SingleOrDefault();

                if (activity != null)
                {
                    if (activity.Rating == null)
                    {
                         activity.Rating = rating;
                         activity.RatingCount++;
                    }
                    else
                    {
                         activity.Rating = (activity.Rating *
activity.RatingCount + rating) / ++activity.RatingCount;
                    }

                         context.SaveChanges();
                    }
               }
          }



  Note: RateActivity retrieves the activity to update, updates it and then calls savesChanges()
  method to update the data source.
  There are some concurrency considerations to make when updating an asset in the database
  which are out of the scope of this lab, for more information you can visit:
  http://msdn.microsoft.com/en-us/library/bb738618.aspx



6. Add a method named Search which will search for the activities that match certain criteria.


                                         Page 43
        (Code Snippet – PlanMyNight MVC App – ActivitiesRepository SearchMethod)
        C#
                 public PagingResult<Activity> Search(ActivitySearchCriteria criteria)
                 {

                    using (var context = new PlanMyNightEntities())
                    {
                        IEnumerable<Activity> query =
        SearchHelper.ActivitySearchQuery(criteria, context.Activities);

                           switch (criteria.SortBy)
                           {
                               case SortCriteria.ActivityType:
                                   query = query.OrderBy(a => a.ActivityTypeId).ThenBy(a
        => a.Name);
                                break;
                            case SortCriteria.Rating:
                                query = query.OrderByDescending(a =>
        a.Rating).ThenBy(a => a.Name);
                                break;
                            default:
                                query = query.OrderBy(a => a.Name);
                                break;
                        }

                        return SearchHelper.PageResults(query, criteria.Page,
        criteria.PageSize);
                    }
                }



         Note: Search method delegates the searching action to the SearchHelper. After the search
         completes, the method sorts the results to match the SortingCriteria, and finally delegates the
         Paging functionality to the SearchHelper again.



       7. Press CTRL+SHIFT+B to build the solution.



Task 3 – Updating Controllers to use Activities Repository
In this task you will update both Home and ActivitiesController to use the ActivitiesRepository instead
of the ActivitiesStubRepository.
       1. Open HomeController.cs by double-clicking over it in the Solution Explorer.




                                                Page 44
       2. Modify the default constructor to create a new instance of ActivitiesRepository instead of
          creating an ActivitiesStubRepository.
        C#
                  public HomeController() :
                      this(new ActivitiesRepository())
                  {
                  }



       3. Open ActivitiesController.cs by double-clicking over it in the Solution Explorer.
       4. Modify the default constructor to create a new instance of ActivitiesRepository instead of
          creating an ActivitiesStubRepository.
        C#
                  public ActivitiesController() :
                      this(new ActivitiesRepository())
                  {
                  }



          Note: Notice how simple it is to change the Repository; this is thanks to the uncoupled
          architecture provided by the MVC pattern. No changes needed to be made to the Views, nor
          to the Controllers, beyond changing the default constructor implementation.



       5. Press CTRL+SHIFT+B to build the solution.



Next Steps
Exercise 2: Verification

Exercise 2: Verification
In this verification you will check that you have correctly performed all steps of the exercise by checking
that the application works with the ActivitiesRepository as it did with the ActivitiesStubRepository.
       1. Press CTRL + F5 to run the program without debugging.




                                                 Page 45
Figure 16
PlanMyNight HomePage


2. Select OH as state and click Search. The Repository will return the list of Activities in Ohio.




                                          Page 46
Figure 17
Search Results


3. Click on an Activity (on its name) and the details of the activity will appear.
4. Rate the activity by selecting a rating value, and clicking Rate.




                                          Page 47
Figure 18
Rating an Activity


5. Click on Search in the Navigation Bar to go to the homepage.
6.   Search for the activities in Ohio by selecting OH as state and hitting Search. The rating will
     be showed in the SearchResults panel.




                                           Page 48
Figure 19
Rating shown in search result


7. Close the browser and stop the ASP.Net development server to stop the application.
    a. In the tray bar, right-click over the ASP.Net Development Server Icon
    b. Select Stop.




Figure 20
Stopping ASP.Net Development Server



                                        Page 49
       8. Press Control+F5 to start the application again
       9. Select OH as state and click Search. You will see that rating was persisted.




        Figure 21
        Persistent Rating


       10. Feel free to use the application and test the sorting, paging, validation and filtering
           functionality.


Next Step
Exercise 3: Adding AJAX for searching activities




                                                   Page 50
Exercise 3: Adding AJAX for searching
activities
In this exercise you will learn how to add AJAX to an existing ASP.NET MVC Application in an unobtrusive
way, without changing the views.
The way you will be adding AJAX will allow the application to continue to work even if JavaScript is
disabled.


Task 1 – Adding ASP.NET 4.0 AJAX Preview 5
In this task you will download ASP.NET 4.0 AJAX Preview 5 Client Templates and add them to your
PlanMyNight solution.
Among other controls and features, ASP.NET 4.0 AJAX Preview 5, includes the Sys.UI.DataView control
that will be used on a future task.

 Note: For more information on this preview please visit:
 http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=32770



       1. Download ASP.NET 4.0 AJAX Preview 5 source code from:
          http://aspnet.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=32770
       2. Extract the downloaded zip file to the
          %TrainingKitInstallFolder%\Labs\AspNetMvcPlanMyNight\Source\Assets folder
       3. Start Microsoft Visual Studio 2010 from Start | All Programs | Microsoft Visual Studio
          2010.

         Note: To attach the database to SQL, Visual Studio needs to be started with administrator
         privileges. To do so, right-click the link in the start menu and select Run as Administrator



       4. Open the solution file Begin.sln located in the folder
          %TrainingKitInstallFolder%\Labs\AspNetMvcPlanMyNight\Source\Ex02-
          CreatingEFDataModel\Begin, and build the solution. Alternatively, you can continue working
          with the solution obtained after completing the previous exercise.
       5. Delete MicrosoftAjax.js file provided by default.
            a. In the Solution Explorer, right-click the MicrosoftAjax.js file, located inside the Scripts
               folder.



                                                  Page 51
            b. Click Delete.
            c. When prompted, confirm the action by choosing OK.
       6. Add MicrosoftAjax.js and MicrosoftAjaxTemplates.js downloaded scripts to your solution.
            a. In the Solution Explorer, right-click the Scripts folder.
            b. Point to Add, and click Existing Item.
            c. Browse to the
               %TrainingKitInstallFolder%\Labs\AspNetMvcPlanMyNight\Source\Assets\ folder
            d. Select MicrosoftAjax.js and MicrosoftAjaxTemplates.js click Add.

                  Note: if you need to debug JavaScript, you may also want to add
                  MicrosoftAjax.debug.js and MicrosoftAjaxTemplates.debug.js, and use them instead.




Task 2 – Creating ClientTemplatesSearchResults View
In this task, you will create a new view, which will be the “dynamic version” of the
ActivitiesSearchResult view. This view will be used to render the search results using JavaScript.
       1. Create ClientTemplatesSearchResults MVC View User Control.
               a. In the Solution Explorer, right-click the Home folder, located under Views.
               b. Point to Add, and click New Item.
               c. Select MVC View User Control under Visual C#.
               d. Type ClientTemplatesSearchResults.ascx as user control Name and click Add.

                  Note: ClientTemplatesSearchResults will contain all the templates that will be used to
                  render the search result. These templates will be populated with data and rendered
                  using JavaScript.



       2. Open ClientTemplatesSearchResults.ascx, if not already opened, and add the following
          bolded code below the document header:
        (Code Snippet – PlanMyNight MVC App – ClientTemplatesSearchResults SearchResultDiv)
        ASP.NET
        <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>

        <style type="text/css">
        </style>



                                                  Page 52
<div id="searchResultsDynamic" class="panel searchResults"
style="display:none">
    <div class="innerPanel">
        <h2>
             <ul>
                  <li class="linkActivities">
                      <strong>Activities</strong>
                  </li>
             </ul>
        </h2>

        <div id="resultsTemplateContainer">
            <div class="items loading"></div>
            <div class="toolbox"></div>
        </div>
    </div>
</div>

<%-- Templates --%>
<div style="display:none" id="templates">

     <%-- Activities template --%>
     <div id="dynamicActivitiesResults" style="display:none;">

    </div>
</div>



 NOTE: In the code above, you have defined the Templates containers for the search results.
 You have also created the “resultsTemplateContainer” that will be used to render the AJAX
 preloader image.



3. Add the sorting controls to ClientTemplatesSearchResults.ascx to provide the end-user with
   sorting functionality.
    a. Insert the following code below the dynamicActivitiesResult div definition:
        (Code Snippet – PlanMyNight MVC App – ClientTemplatesSearchResults SortingControls)
        ASP.NET
        <%-- Activities template --%>
        <div id="dynamicActivitiesResults" style="display:none;">
            <div id="dynamicActivitiesSort" class="sys-template subheader">
                Sort by:
                <strong sys:if="$dataItem.orderBy=='Name'">Name</strong>
                <a sys:if="$dataItem.orderBy!='Name'" sys:href="{binding null,
        convert=sortByConverter, defaultValue=Name}">Name</a>



                                       Page 53
                |
                <strong sys:if="$dataItem.orderBy=='Rating'">Rating</strong>
                <a sys:if="$dataItem.orderBy!='Rating'" sys:href="{binding null,
        convert=sortByConverter, defaultValue=Rating}">Rating</a>
                |
                <strong sys:if="$dataItem.orderBy=='ActivityType'">Type</strong>
                <a sys:if="$dataItem.orderBy!='ActivityType'" sys:href="{binding
        null, convert=sortByConverter, defaultValue=ActivityType}">Type</a>
            </div>



  Note: The code you have just added will render the different sorting options (Rating, Type and
  Name.) dynamically.
  If the items are sorted using the sorting criteria which is being rendered, the view will render
  the sort option in a <strong> tag; otherwise it will render a link, generated dynamically using
  JavaScript’s sortByConverter function.



4. Add the bolded code that follows, below the sorting controls.
(Code Snippet – PlanMyNight MVC App – ClientTemplatesSearchResults SearchResults)
ASP.NET
        </div>
        <div class="items">
            <h3 id="dynamicActivitiesEmpty" style="display:none">No activities
found...</h3>
            <ul id="dynamicActivitiesItems" class="sys-template activities" >
                <li>
                     <span class="off id">{{ Id }}</span>
                     <h3>
                          <span class="rating"><span>Rating: </span>{binding
Rating, convert=ratingConverter}</span>
                          <a sys:href="{binding Id,
convert=activityDetailsLinkConverter}">{{ Name }}</a>
                     </h3>
                     <p>{{ Street }} | {{ City }}, {{ State }} {{ Zip }} |
Phone: {{ PhoneNumber }}</p>
                </li>
            </ul>
        </div>



  Note: The code added provides two different template options.




                                          Page 54
  The first one renders the message: “No activities found…” if the Item count is 0, while the
  second one shows the Search Results. The decision of which one to show will be done via
  JavaScript.
  Notice that there is no template to render the message “Please provide a search criteria...”
  this is because you will validate the search parameters using JavaScript, allowing to perform
  the search request only if the required parameters are provided.



5. To Complete the ClientTemplatesSearchResults user control, add the template that will be
   used to render the paging controls. Copy the following code below the “Items”closing <div>
(Code Snippet – PlanMyNight MVC App – ClientTemplatesSearchResults PagingControls)
ASP.NET
</div>

<div class="toolbox">
    <div class="pager">
        <span id="dynamicActivitiesPager" class="sys-template">
            <span sys:if="$index==0">
                 <strong sys:if="$dataItem.currentPage===1">«</strong>
                 <a sys:if="$dataItem.currentPage!==1" sys:href="{binding
currentPage, convert=previousPageConverter}" class="ajaxSearch">«</a>
                 |
            </span>
            <strong sys:if="$dataItem.currentPage===$dataItem.pageNumber">{{
pageNumber }}</strong>
            <a sys:if="$dataItem.currentPage!==$dataItem.pageNumber"
sys:href="{binding pageNumber, convert=pageConverter}" class="ajaxSearch">{{
pageNumber }}</a>
             |
             <span sys:if="$dataItem.totalPages==$dataItem.pageNumber">
                   <strong
sys:if="$dataItem.currentPage===$dataItem.pageNumber">»</strong>
                   <a sys:if="$dataItem.currentPage!==$dataItem.pageNumber"
sys:href="{binding currentPage, convert=nextPageConverter}"
class="ajaxSearch">»</a>
             </span>
         </span>
    </div>
</div>



  Note: The code above provides the template for rendering the paging controls dynamically.
  The link URL will be generated via JavaScript, using the previousPageConverter,
  nextPageConverter and pageConverter.



                                         Page 55
       6. Modify Index.aspx file to render ClientTemplatesSearchResults user control.
            a. In the Solution Explorer, double-click the Index.aspx file, located under Views\Home, to
               open it.
            b. Before IndexContent asp:Content closing tag, paste the following bolded code:
                  ASP.NET
                      <% Html.RenderPartial("ClientTemplatesSearchResults"); %>

                  </asp:Content>




Task 3 – Creating Search Scripts
In this task you will create the script which will intercept the Search Action, and replace it with an AJAX
request.
Other scripts such as converters and helper functions will be created in this task.
       1. Create ajax.search.js javascript file.
            a. In the Solution Explorer, right-click the Scripts folder.
            b. Point to Add, and click New Item.
            c. Select JScript File, under Web.
            d. Type ajax.search.js as Name and click Add.
       2. Bind a delegate to the init event of your application by pasting the following code into the
          ajax.search.js file
        (Code Snippet – PlanMyNight MVC App – ajax.search Init Event Delegate)
        JScript
        var isSearching = false;

        Sys.Application.add_init(function() {
            // history
            Sys.Application.set_enableHistory(true);
            Sys.Application.add_navigate(function(sender, e) {
                if (isSearching) return;

                // invoke search
                var criteria = e.get_state();
                var searchUrl = $("#searchForm form")[0].action + "?" +
        Sys.Application._serializeState(criteria);



                                                   Page 56
          updateModel(searchUrl, onSearchRequest, onSearchResponse);

        // select fields
        selectedTypeIdOpt = $("#ActivityTypeId option[value=" +
criteria.ActivityTypeId + "]");
        selectedStateOpt = $("#State option[value=" + criteria.State + "]");
        if (selectedTypeIdOpt.length == 0) selectedTypeIdOpt =
$("#ActivityTypeId option:first-child");
        if (selectedStateOpt.length == 0) selectedStateOpt = $("#State
option:first-child");
        selectedTypeIdOpt[0].selected = true;
        selectedStateOpt[0].selected = true;
        $("#City").val(criteria.City ? criteria.City : "");
        $("#Zip").val(criteria.Zip ? criteria.Zip : "");

          document.title = getPageTitle(criteria);
    });

    // activity templates
    $create(Sys.UI.DataView, null, null, null,
$get("dynamicActivitiesItems"));
    $create(Sys.UI.DataView, null, null, null,
$get("dynamicActivitiesPager"));
    $create(Sys.UI.DataView, null, null, null, $get("dynamicActivitiesSort"));

    // auto-search on change
    function autoRefresh() {
        isSearching = false;    // force cancel
        var form = $("#searchForm form");
        var url = form[0].action + "?" + form.serialize();
        updateModel(url, onSearchRequest, onSearchResponse);
    };

    $("#State").bind("change", autoRefresh);
    $("#ActivityTypeId").bind("change", autoRefresh);
    $("#City").bind("change", autoRefresh);
    $("#Zip").bind("change", autoRefresh);
    $("#StreetAddress").bind("change", autoRefresh);

    // intercept form to prevent from posting, and enable ajax calls instead
    updateModelOnFormSubmit($("#searchForm form"), onSearchRequest,
onSearchResponse);

    // intercept pager links to prevent them from loading, and enable ajax
calls instead
    $(".searchResults .pager a").each(function() {
        updateModelOnClickLink($(this), onSearchRequest, onSearchResponse)
    });
});



                                  Page 57
 Note: In the code above you have binded a delegate to the init event of your ASP.NET MVC
 application.
 This delegate has three main parts:
 - On the first one, it binds a delegate to the navigation properties. This delegate executes a
 search action when browsing back and forward in the browser.
 - The second one creates three DataView controls which will be used to store the search
 results, and binds the autoRefresh() function to all the input boxes in the SearchForm.
     autoRefresh simply executes a search action with the updated parameters.
 - Finally on the third part, you are replacing the form POST and the pager links with an AJAX
 call instead.



3. Create onSearchRequest function which will be invoked before the XML HTTP request.
(Code Snippet – PlanMyNight MVC App – ajax.search onSearchRequest)
JScript
function onSearchRequest(href) {
    if (isSearching) return false;

    // validation
    var criteria = Sys.Application._deserializeState(href.split("?")[1]);
    if (!criteria || (!criteria.StreetAddress && !criteria.State &&
!criteria.City && !criteria.Zip)) return false;

      isSearching = true;

    if (!criteria.page) criteria.page = null;
    if (!criteria.ActivityTypeId || criteria.ActivityTypeId == "0")
criteria.ActivityTypeId = null;
    Sys.Application.addHistoryPoint(criteria, getPageTitle(criteria));

      // preloading
      $("#dynamicActivitiesEmpty").hide();
      $(".searchResults .items").addClass("loading");
      $(".searchResults .pager").addClass("disabled");
      $("#searchForm .field-validation-error").hide();
      return true;
};




                                        Page 58
 Note: onSearchRequest will perform simple validation; if it fails, the function cancels the
 request; if it succeeds, it adds a history point for the current search and lets the request
 continue.



4. Create onSearchResponse function which will be invoked after data is retrieved and binded.
(Code Snippet – PlanMyNight MVC App – ajax.search OnSearchResponse)
JScript
function onSearchResponse(data) {
    isSearching = false;

     var orderBy = $("#searchForm input[name=SortBy]").val();
     if (orderBy == "") orderBy = "Name";

     $("#searchResultsStatic").remove();
     $("#searchResultsDynamic").show();
     $("#searchResultsDynamic h2 ul").attr("class", "active");

    // move active template to templates container
    $("#resultsTemplateContainer
#dynamicActivitiesResults").remove().appendTo($("#templates"));

     var template = template = $("#dynamicActivitiesResults");
     if (data.TotalItems == 0) $("#dynamicActivitiesEmpty").show();

     // rebind items
     $find("dynamicActivitiesItems").set_data(data.Items);

     // rating labels
     showRatingLabels($("#dynamicActivitiesItems"));

     // rebind pager
     refreshPager("dynamicActivitiesPager", data);

    // rebind sort
    $find("dynamicActivitiesSort").set_data({ orderBy: orderBy, timestamp: new
Date() });
    $("#dynamicActivitiesSort a").each(function(ix, el) {
        el.onclick = function() {
            var url = this.href.split("#")[0];     // strip anchors
            var sortBy = url.substring(url.indexOf('SortBy=') +
7).split('&')[0]
            $("#searchForm input[name='SortBy']").val(sortBy);
            updateModel(url, onSearchRequest, onSearchResponse,
$find("dynamicSearchResults"));
            return false;
        }


                                         Page 59
       });

       // remove from templates and set into active container
       template.remove();
       $("#resultsTemplateContainer").empty().append(template);
       template.show();

       // hide loading
       $(".searchResults .items").removeClass("loading").scrollTop(0);
       $(".searchResults .items li").hide().fadeIn();
       $(".searchResults .pager").removeClass("disabled");
};



    Note: onSearchResponse will update the templates and bind the SearchResults, Pager and
    Sorting controls with the data obtained from the HomeController.



5. Add the refreshPager function that will update the pager values and links, with the data
   obtained from the HomeController after a search finishes.
(Code Snippet – PlanMyNight MVC App – ajax.search RefreshPager)
JScript
function refreshPager(pagerId, resultsData) {
    // pager
    var pages = [];
    for (var i = 1; i <= resultsData.TotalPages; i++) {
        pages.push({ pageNumber: i, currentPage: resultsData.CurrentPage,
totalPages: resultsData.TotalPages });
    }

       $find(pagerId).set_data(pages);
       $("#" + pagerId + " a").each(function(ix, el) {
           el.onclick = function() {
               var url = this.href.split("#")[0];      // strip anchors
               updateModel(url, onSearchRequest, onSearchResponse);
               return false;
           }
       });
}



6. Add the showRatingLabels function that will decorate the rating span, with the class name
   of its rating value.
(Code Snippet – PlanMyNight MVC App – ajax.search ShowRatingLabels)



                                        Page 60
JScript
function showRatingLabels(container) {
    container.find("span.rating").each(function() {
        var $el = $(this);
        $el.find("span").remove();
        if ($el.text() == "0.0") {
            $el.remove();
        }
        else {
            $el.addClass("rating rating_" + $el.text().replace(".", "_"));
        }
    });
}



 Note: The class name contains identifies the span, and it is used in the Style sheets to render
 the rating image appropriately.



7. Add the updateModelOnClickLink, updateModelOnFormSubmit, and updateModel
   functions.
(Code Snippet – PlanMyNight MVC App – ajax.search updateModelOnClickLink)
JScript
function updateModelOnClickLink($link, callbackRequest, callbackResponse,
targetModel) {
    $link.click(function() {
        var url = this.href.split("#")[0];     // strip anchors
        updateModel(url, callbackRequest, callbackResponse, targetModel);
        return false;
    });
}



(Code Snippet – PlanMyNight MVC App – ajax.search updateModelOnFormSubmit)
JScript
function updateModelOnFormSubmit($form, callbackRequest, callbackResponse,
targetModel) {
    $form.bind("submit", function() {
        var url = this.action + '?' + $(this).serialize();
        updateModel(url, callbackRequest, callbackResponse, targetModel);
        return false;
    });
}




                                        Page 61
(Code Snippet – PlanMyNight MVC App – ajax.search updateModel)
JScript
function updateModel(url, callbackRequest, callbackResponse, targetModel) {
    if (!callbackRequest(url)) return false;
    $.ajax({
        url: url,
        method: "GET",
        type: this.method,
        dataType: "json",
        beforeSend: function(xhr) { xhr.setRequestHeader("Content-Type",
"application/json"); },
        success: function(json) {
             if (targetModel) targetModel.set_data(json);
             callbackResponse(json);
        }
    });
}



 Note: updateModel performs the AJAX call to a given URL, invoking onSearchRequest before
 the call, and onSearchResponse after the JSON Response is received.
 Although this function is created receiving the function to invoke before the AJAX call, and
 after the response is received as parameters, they are only invoked with onSearchRequest and
 onSearchResponse.



8. Add the following helper methods:
(Code Snippet – PlanMyNight MVC App – ajax.search HelperMethods)
JScript
// helpers
function getPageTitle(criteria) {
    var descriptions = [];
    if (criteria.ActivityTypeId != null)
descriptions.push($('#searchForm').find('#ActivityTypeId
option:selected').text());
    if (criteria.City != null) descriptions.push(criteria.City);
    if (criteria.State != null) descriptions.push(criteria.State);
    if (criteria.Zip != null) descriptions.push(criteria.Zip);
    descriptions.push('Page ' + (criteria.page || "1"));
    return 'Plan My Night - Search: ' + descriptions.join(' | ');
};

function replaceQueryString(url, param, value) {
    var re = new RegExp("([?|&])" + param + "=.*?(&|$)", "i");
    if (url.match(re))


                                       Page 62
           return url.replace(re, '$1' + param + "=" + value + '$2');
       else
           return url + '&' + param + "=" + value;
}



    Note: getPageTitle parses the search criteria to be shown as the page title.
    replaceQueryString injects a query string parameter and value into an URL.



9. Add the following converters:
(Code Snippet – PlanMyNight MVC App – ajax.search Converters)
JScript
// converters
function ratingConverter(value) {
    var val = (Math.round((value * 2)) / 2).toString();
    if (val % 1 == 0) {
        return val.toString() + ".0";
    } else {
        return val.toString()
    }
}

function activityDetailsLinkConverter(value) {
    return baseUrl + "Activities/Details/" + value;
}

function sortByConverter(value, o) {
    var $form = $("#searchForm form");
    var formAction = $form[0].action + "?" + $form.serialize();
    formAction = replaceQueryString(formAction, "SortBy",
o.get_defaultValue());
    return formAction;
}

function pageConverter(value) {
    var $form = $("#searchForm form");
    return $form[0].action + "?" + $form.serialize() + "&page=" + value;
}

function previousPageConverter(value) {
    return pageConverter(value - 1);
}

function nextPageConverter(value) {
    return pageConverter(value + 1);


                                           Page 63
        }



            Note: In the code above you added 6 different converters used to render the links in
            ClientTemplatesSearchResults.ascx correctly or to add CSS classes dynamically.
            - ratingConverter: converts the rating value to match the format of the rating class (adding a
            “.0” if the value is an integer.)
            - activityDetailsLinkConverter: creates the URL used when rendering the link to the activities
            details view.
            - sortByConverter: creates the URL used when rendering the sorting links.
            - pageConverter: creates the URL used to when rendering the paging controls.
            - previousPageConverter: simply calls pageConverter current page value -1.
            - nextPageConverter: simply calls pageConverter current page value +1.




Task 4 – Creating AJAX Client Script Manager
In this task you will create a Script Manager to provide a set of Extension Methods to the AjaxHelper, in
order to register and manage AJAX Script files.

 Note: using the script manager will optimize the usage of scripts, allowing all scripts to be registered by
 each view as required, but rendered only once and still be accessible throughout the application.
 You could enhance the Script Manager to render the scripts in a minified way (removing white spaces,
 break-lines, comments, etc.,) making them more lightweight. Or you could render all scripts as a whole
 in order to minimize the HTTP requests for the sake of performance. But that is out of the scope of this
 lab.



       1. Create a new class named AjaxClientScriptExtensions.cs
                  a. In the Solution Explorer, right-click the ViewModels folder.
                  b. Point to Add, and click Class.
                  c. Type AjaxClientScriptExtensions.cs as class Name and click Add.
                  d. Replace the default class implementation with the following:
                  (Code Snippet – PlanMyNight MVC App – AjaxClientScriptExtensions ClassHeader)
                  C#
                  namespace PlanMyNight.ViewModels


                                                      Page 64
        {
              using   System;
              using   System.Collections.Generic;
              using   System.Globalization;
              using   System.Linq;
              using   System.Text;
              using   System.Web.Mvc;
              using   System.Web.UI;

              public static class AjaxClientScriptExtensions
              {
              }
        }



2. Create an Extension Method named JavaScriptEnabled:
C#
        public static bool JavaScriptEnabled(this AjaxHelper ajaxHelper) {
            return
System.Web.HttpContext.Current.Request.Browser.EcmaScriptVersion.Major >= 1;
        }



  Note: JavaScriptEnabled will check if JavaScript is enabled in the current context and return
  true or false accordingly.



3. Inside AjaxClientScriptExtensions class, create a private class named ScriptIncludeReference
   that will wrap the URL of the registered script.
(Code Snippet – PlanMyNight MVC App – AjaxClientScriptExtensions ScriptIncludeReference
Class)
C#
            private class ScriptIncludeReference
            {
                public ScriptIncludeReference(string url)
                {
                    this.Url = url;
                }

                public string Url { get; private set; }

            public string Render()
            {
                return string.Format(CultureInfo.InvariantCulture, "<script
type=\"text/javascript\" src=\"{0}\"></script>", this.Url);


                                         Page 65
              }
         }



 Note: ScriptIncludeReference simply provides a wrapper to the script registered. It stores the
 URL on a property and exposes a Render method used to render the script into the view.



4. Create a private static method, and name it GetScriptReferences.
(Code Snippet – PlanMyNight MVC App – AjaxClientScriptExtensions GetScriptReferences)
C#
        private static List<ScriptIncludeReference>
GetScriptReferences(AjaxHelper ajaxHelper)
        {
            var contextItems = ajaxHelper.ViewContext.HttpContext.Items;
            if (!contextItems.Contains("AjaxClientScripts"))
            {
                contextItems["AjaxClientScripts"] = new
List<ScriptIncludeReference>();
            }

            return
(List<ScriptIncludeReference>)contextItems["AjaxClientScripts"];
        }



 Note: GetScriptReferences retrieves the registered scripts from the ajaxHelper’s ViewContext.



5. Create an Extension Method named RegiterClientScriptInclude.
(Code Snippet – PlanMyNight MVC App – AjaxClientScriptExtensions RegisterClientScriptInclude)
C#
        public static void RegisterClientScriptInclude(this AjaxHelper
ajaxHelper, string url)
        {
            var scripts = GetScriptReferences(ajaxHelper);
            if (scripts.OfType<ScriptIncludeReference>().FirstOrDefault(s =>
s.Url == url) == null)
            {
                scripts.Add(new ScriptIncludeReference(url));
            }
        }




                                       Page 66
  Note: RegiterClientScriptInclude is in charge of registering a new script in ajaxHelper’s
  ViewContext. If the script is already present, it will do nothing.



6. Create an Extension Method named RenderClientScripts to render all the registered scripts.
(Code Snippet – PlanMyNight MVC App – AjaxClientScriptExtensions RenderClientScripts)
C#
          public static string RenderClientScripts(this AjaxHelper ajaxHelper)
          {
              StringBuilder sb = new StringBuilder();
              foreach (var reference in GetScriptReferences(ajaxHelper))
              {
                  sb.Append(reference.Render());
              }

               return sb.ToString();
          }



7. In order for the scripts to be available throughout the application, we need to render them
   in Site.Master. Add the code to do this before <body> closing tag.
     a. In the Solution Explorer, double-click the Site.Master file, located inside Views\Shared
        folder.
     b. Paste the bolded code that follows before the <body> closing tag.
         ASP.NET
              <script type="text/javascript">
                  var baseUrl = '<%=Url.Content("~/")%>';
              </script>
              <%= Ajax.RenderClientScripts() %>

         </body>
         </html>



8. Finally, add the code to register all the scripts required by ClientTemplateSearchResults.ascx
   user control.
     a. In the Solution Explorer, double-click the ClientTemplateSearchResults.ascx file, located
        inside Views\Home folder, to open it.
     b. Below the control header, add the code to register the required scripts:
         ASP.NET
         <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>


                                         Page 67
               <% Ajax.RegisterClientScriptInclude(Url.Content("~/Scripts/jquery-
               1.3.2.min.js")); %>
               <%
               Ajax.RegisterClientScriptInclude(Url.Content("~/Scripts/MicrosoftAjax.js
               ")); %>
               <%
               Ajax.RegisterClientScriptInclude(Url.Content("~/Scripts/MicrosoftAjaxTem
               plates.js")); %>
               <%
               Ajax.RegisterClientScriptInclude(Url.Content("~/Scripts/ajax.search.js")
               ); %>




Task5 – Modifying HomeController to use AJAX
In this task, you will modify the HomeController Search method to return a JSON Response if the
request was an AJAX call.
       1. In the Solution Explorer, double-click the HomeController.cs file, located inside the
          Controllers folder.
       2. Add a property named Ajax that will return a Boolean, indicating if the request was Ajax or
          not.
       C#
               private bool Ajax
               {
                   get
                   {
                       return !string.IsNullOrEmpty(this.Request.ContentType) &&
       this.Request.ContentType.Contains("application/json");
                   }
               }



       3. In the Search method, replace the code after setting the activities into the view data with
          the bolded code that follows.
       (Code Snippet – PlanMyNight MVC App – AjaxClientScriptExtensions Search Method)
       C#
                      // search activities
                      var activities = this.activitiesRepository.Search(searchCriteria);
                      this.ViewData["PagingResult"] = activities;

                      if (this.Ajax)


                                                Page 68
                           {
                                  // json
                                  return new JsonResult()
                                  {
                                      JsonRequestBehavior = JsonRequestBehavior.AllowGet,
                                      Data = activities
                                  };
                           }
                           else
                           {
                                  this.ViewData["SearchCriteria"] = searchCriteria;
                                  this.InjectActivitySearchFields(this.ViewData,
        searchCriteria);
                        this.ViewData["CriteriaDescription"] =
        this.GetCriteriaDescription(searchCriteria, " | ");
                        this.ViewData["KeywordsMetatag"] =
        this.GetCriteriaDescription(searchCriteria, ", ");
                        return View("Index");
                    }



          Note: The code returns the activities as a JsonResult if the request was Ajax, or as it did
          previously if it’s not.



       4. Press CTRL+SHIFT+B to build the solution.




Next Steps
Exercise 3: Verification

Exercise 3: Verification
In this verification you will check that you have correctly performed all steps of the exercise by testing
the application both with JavaScript enabled and disabled.
       1. Press CTRL + F5 to run the program without debugging.




                                                  Page 69
Figure 22
PlanMyNight HomePage


2. Select OH as state and click Search.




                                          Page 70
Figure 23
AJAX Request



 Note: The request is made through AJAX, and the results are rendered without reloading the
 whole page.



3. Disable JavaScript in the browser
    a. In Internet Explorer 8, go to the Tools >> Internet Options menu item.
    b. Open the Security tab and click the Custom level button
    c. Under Scripting, set Active scripting to Disable.




                                        Page 71
  Figure 24
  Disabling JavaScript


  4. Browse to http://localhost:50000
  5. Select OH as state and click Search

    Note: The request is being made through basic http and the whole page is rendered to show
    the search results.



  6. Feel free to use the application and test the sorting, paging, validation and filtering
     functionality using AJAX.




Summary
                                            Page 72
By completing this Hands-On Lab you have learnt how to create a fully functional MVC application, with
paging, filtering, sorting and simple validation functionality. You have also learnt how to add an Entity
Framework Data Provider and AJAX to an existing MVC Web Application.




                                                 Page 73

								
To top