ASP.NET Core Basics: Blazor

Blazor is a single page application framework that uses .NET running in the browser through the magic of WebAssembly and Mono. Check out How Blazor runs .NET in the browser for more information. You can think of Blazor like you do Angular or React, but written in .NET.

Please make note that this project is still an unsupported experiment and shouldn’t be used for production applications. This is part of the reason I held off on trying it until now.

I will be adding the Blazor project to my ASP.NET Core Basics repo. The code before any changes can be found here.

Getting Started

For an experiment, Blazor already has some really good docs. A lot of this post is going to mirror the Get started with Blazor docs page, but I try and document as I go. First, make sure you have the latest version of .NET Core 2.1.x SDK installed.

If you are going to be using Visual Studio you will need at least version 15.7. In addition, you will need to install the Blazor Language Services extension. From within Visual Studio, this can be done from the Tools > Extension and Updates menu. Select Online and search for Blazor. It should come up with just one item and you can click Download to start the install process.

Project Creation

We are going to use the .NET CLI to install the Blazor templates and add the resulting project to the ASP.NET Core Basics solution. First, open a command prompt and use the following command to get the Blazor template installed.

dotnet new -i Microsoft.AspNetCore.Blazor.Templates

If you are using the sample code I am running the commands from a new Blazor directory in the existing src directory. Then run the following command to create the Blazor project.

dotnet new blazor

The next command will add the new project to the existing solution. The path to the solution file isn’t required if the command is being run from the same directory as the solution file, but in our case, we are running the commands two level down from where the solution file is located.

dotnet sln "../../ASP.NET Core Basics.sln" add Blazor.csproj

At this point, you can use the following command to run the application.

dotnet run

Adding the Contact List

As with the other post in the ASP.NET Basics category, we are going to add a contact list page that pulls a list of contacts from an API. For this example, the API is part of the Contacts project that already exists in the sample solution. All the changes in the section take place in the Blazor project we created above.

In the Pages directory add a ContactList.cshtml file with the following contents. A break down of some of the parts of this file will follow.

@page "/contacts"
@inject HttpClient Http

<h1>Contact List</h1>

@if (contacts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var contact in contacts)
            {
                <tr>
                    <td>@contact.Id</td>
                    <td>@contact.Name</td>
                </tr>
            }
        </tbody>
    </table>
}

@functions {
    Contact[] contacts;

    protected override async Task OnInitAsync()
    {
        contacts = await Http.GetJsonAsync<Contact[]>("http://localhost:13322/api/contactsApi/");
    }

    class Contact
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string PostalCode { get; set; }
        public string Phone { get; set; }
        public string Email { get; set; }
    }
}

This has a lot going on so we are going to start with the functions section which is essentially a way to block off code needed by the page. It can contain functions, classes, or whatever else C# construct the page may need. The following is just the functions section of the above page.

@functions {
    Contact[] contacts;

    protected override async Task OnInitAsync()
    {
        contacts = await Http.GetJsonAsync<Contact[]>("http://localhost:13322/api/contactsApi/");
    }

    class Contact
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string PostalCode { get; set; }
        public string Phone { get; set; }
        public string Email { get; set; }
    }
}

In our sample, we are using the functions to define a Contact class to hold the data we get back from the API call in the OnInitAsync function. The OnInitAsync function is one of the lifecycle functions provided by the Blazor components. We aren’t going to dive into components, I recommend you check out the component docs for more information.

The @page "/contacts" is defining the route that the page will be served for. Next, @inject HttpClient Http is showing how to use ASP.NET Core’s dependency injection to get an instance of an HttpClient.

The remaining bit of the file is normal Razor. Everything defined in the functions section is available for use in your layout. For example, we are using the list of contacts filled by the OnInitAsync to loop and create a table to display our contact list.

<h1>Contact List</h1>

@if (contacts == null)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>ID</th>
                <th>Name</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var contact in contacts)
            {
                <tr>
                    <td>@contact.Id</td>
                    <td>@contact.Name</td>
                </tr>
            }
        </tbody>
    </table>
}

Wrapping Up

Blazor is a very interesting concept. I hope it is able to make it through the experimental phase into a supported project. It seems like it would be a great option for .NET developers. It was a lot of fun to finally try out. Go give it a try and make sure and provide feedback to Microsoft so they can make an informed decision on how to proceed with the project.

The finished code can be found here.

Run Multiple Projects in Visual Studio Code

While expanding the sample used in the Electron.NET post to include an API I hit a snag with Visual Studio Code. I have used multiple projects with Visual Studio Code before, but never two distinct applications that I need to run at the same time.  Sample code that contains the two projects, but before any modifications covered in this post can be found here.

Building Multiple Projects

The first issue to tackle was getting both projects to build since they are technically independent. As I’m sure you are aware VS Code doesn’t need a solution file like full Visual Studio does. What I learned during this process was that while a solution file isn’t required once can be used to ensure multiple projects all get built. Using VS Code’s Terminal I ran the following commands to create a new solution and add my two projects to that solution.

dotnet new sln -n ElectronTest
dotnet sln add src/ElectronTest/ElectronTest.csproj
dotnet sln add src/ContactsApi/ContactsApi.csproj

With the solution file in place, I then opened the tasks.json file found in the .vscode directory. In the build task, I removed the path to a specific csproj file and just used the workspace folder.

Before:
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
    "build",
    "${workspaceFolder}/src/ElectronTest/ElectronTest.csproj"
]

After:
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
    "build",
    "${workspaceFolder}"
]

This is just one option on how to handle the builds. I believe another way would be to have two build tasks (one for each project) and use the individual build task in your launch configurations (which we are going to talk about next).

Debugging Multiple Projects

Open up your launch.json file in the .vscode directory. By default you will see a couple of configurations more than likely you will see one named .NET Core Attach and another named .NET Core Launch (web). It is the .NET Core Launch (web) configuration that we are interested in. This configuration controls how our application is launched. If you notice the program property points to the dll created during the build process, in the sample code this is  ${workspaceFolder}/src/ElectronTest/bin/Debug/netcoreapp2.0/ElectronTest.dll. This is all fine but doesn’t give us a way to run both of our projects. Let’s tweak the env property to set the ASPNETCORE_URLS which will control the URL the application is launched with.

Before:
"env": {
    "ASPNETCORE_ENVIRONMENT": "Development"
}

After:
"env": {
    "ASPNETCORE_ENVIRONMENT": "Development",
    "ASPNETCORE_URLS": "http://localhost:5001"
}

Now that the original application’s launch configuration is set we need to add a second launch configuration for our second application. The following is the full configuration section for my second application (the API).

{
    "name": ".NET Core Launch (API)",
    "type": "coreclr",
    "request": "launch",
    "preLaunchTask": "build",
    "program": "${workspaceRoot}/src/ContactsApi/bin/Debug/netcoreapp2.0/ContactsApi.dll",
    "args": [],
    "cwd": "${workspaceRoot}/src/ContactsApi",
    "stopAtEntry": false,
    "launchBrowser": {
        "enabled": false,
        "args": "${auto-detect-url}",
        "windows": {
            "command": "cmd.exe",
            "args": "/C start ${auto-detect-url}"
        },
        "osx": {
            "command": "open"
        },
        "linux": {
            "command": "xdg-open"
        }
    },
    "env": {
        "ASPNETCORE_ENVIRONMENT": "Development",
        "ASPNETCORE_URLS": "http://localhost:5000"
    },
    "sourceFileMap": {
        "/Views": "${workspaceRoot}/Views"
    }
}

I started with a copy of the original application and just modified a couple of things. First, the name property is now .NET Core Launch (API) which will help us know which application we are launching later. In the launchBrowser section, I set enabled to false since this is an API and I don’t need to launch the browser when the application starts. The final difference is in ASPNETCORE_URLS to make sure the URLs of the two applications are different. I used http://localhost:5000 in this example.

Now that all our configurations are good to go hop over to the debug section in VS Code.

On the debug you will notice that both of our launch configurations are listed. If you select the API one and hit the green play button it will start the API. With the API running you can then select the web configuration and hit the green play button and you will have both of your applications running in debug mode.

Wrapping Up

While it is not obvious at all how to get multiple applications to run in a single instance of VS Code the process isn’t hard after you do it the first time. I think my Visual Studio experience made figuring this out harder than it should have been. My key to learning how to get this going was on this GitHub issue.

Hopefully, this saved you some of the struggles I went through. Please leave a comment if there are different ways to accomplish this. The code with the final configuration can be found here.

Broadcast from an ASP.NET Web API to a UWP Client using SignalR 2

I recently had to take an existing ASP.NET Web API (not core) that was used by a UWP (Universal Windows Platform) client to pull information and modify it to receive notifications from the Web API under some situations. This post is going to cover the creation of a Web API and UWP projects and the modification of the projects to allow notification from the Web API to the UWP client.

Web API Project Creation

In Visual Studio we need to create a new project for the Web API using File > New > Project.

We will be creating an ASP.NET Web Application (.NET Framework) and I’m just naming the project Web API.

On the next screen select that we want a Web API application.

UPW Project Creation

As above in Visual Studio we need to create a new solution and project for the UWP Client using File > New > Project from a new instance of Visual Studio. Select the Blank App (Universal Windows) template.

Next, you will be prompted for the versions of Window 10 to support with the UWP application. I just took the defaults.

You may also want to set your PC into Developer mode.

Add SignalR to the Web API

Now that the initial project creation is done let’s add SignalR to the WebApi project. Inside of Visual Studio right-click on the project and select Manage NuGet Packages.  Select the Browse tab and in the search box enter SignalR and we are looking for the Microsoft.AspNet.SignalR package. Select it and click the Install button.

Configuration

Now that SignalR is installed it needs to be configured on startup of the application. To do this add a Startup class if it doesn’t exist with the following contents.

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var hubConfiguration = new HubConfiguration
        {
            EnableDetailedErrors = true,
            EnableJavaScriptProxies = false
        };
        app.MapSignalR(hubConfiguration);

    }
}

The hubConfiguration bit is only needed if you want to change the default settings. In this example, we don’t have any JavaScript clients so we don’t need those proxies and we are learning so detailed errors will come in handy.

Hub

The first thing we need to do is create a new hub which is what SignalR uses to do all of its magic. Hubs are what enable the server (and/or client depending on how you are using it) to make remote calls. Since we are just doing a broadcast the hub class is empty except for a reference to the class that will be handling when to send the broadcast. This property is very important since it will be the trigger for Broadcaster to get instantiated when a client connects.  The following is the full class.

using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;

namespace WebApi.Hubs
{
    [HubName("BroadcastHub")]
    public class BroadcastHub : Hub
    {
        private readonly Broadcaster _broadcaster = Broadcaster.Instance;
    }
}

Make note of the hub name used as we will need it on the client side.

Next, we have the class that actually does the broadcasting. The following is the full class.

using System;
using System.Threading;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;

namespace WebApi.Hubs
{
    public class Broadcaster
    {
        private readonly TimeSpan _updateInterval = 
            TimeSpan.FromMilliseconds(250);
        private Timer _timer;

        private static readonly Lazy<Broadcaster> _instance = 
            new Lazy<Broadcaster>(() => new Broadcaster(GlobalHost.ConnectionManager.GetHubContext<BroadcastHub>().Clients));
        public static Broadcaster Instance => _instance.Value;

        public IHubConnectionContext<dynamic> Clients { get; set; }

        public Broadcaster(IHubConnectionContext<dynamic> clients)
        {
            Clients = clients;
            _timer = new Timer(Broadcast, null, _updateInterval, _updateInterval);
        }

        public void Broadcast(object state)
        {
            Clients.All.Broadcast(DateTime.Now);
        }
    }
}

The above is a singleton which gets created with the list of clients for a specific hub which can be seen in the following line. It has been reformatted from above to try and make it a bit more readable.

private static readonly Lazy<Broadcaster> _instance = 
  new Lazy<Broadcaster>(() => 
             new Broadcaster(GlobalHost
                            .ConnectionManager
                            .GetHubContext<BroadcastHub>()
                            .Clients));

The constructor of the class sets up a timer that calls a Broadcast function which sends the current date and time to all the connected clients. This could be any sort of data, but for this sample, I’m keeping it simple.

Add SignalR to the UWP Client

Now over in the UWP project, we need to add the SignalR client by right-clicking on the project and selecting Manage NuGet Packages. As in the Web API instructions above search for SignalR using the Browse tab. Select the Microsoft.AspNet.SignalR.Client package and click Install.

UI

To keep the sample as simple as possible we are just going to add a TextBlock to the MainPage.xaml to display the results of the broadcast from the Web API. The following is the full code for the page.

<Page
    x:Class="UwpClient.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:UwpClient"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Name="BroadcastResults" />
    </Grid>
</Page>

Now in the code behind we need to connect to the hub on the Web API and define what happens when a broadcast is received. The following is all of the code in the code behind which will be followed up with some comments.

public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
        InitilizeHub();
    }

    private async void InitilizeHub()
    {
        var hubConnection = new HubConnection("http://localhost:50869");
        var hubProxy = hubConnection.CreateHubProxy("BroadcastHub");

        hubProxy
        .On<DateTime>("Broadcast",
                      async data => 
                            await Dispatcher
                                  .RunAsync(CoreDispatcherPriority.Normal,
                                            () => BroadcastResults.Text = 
                                                          data.ToString()));
        await hubConnection.Start();
    }
}

The hubConnection as you might expect controls the connection with the server and the hubProxy deals with the interaction with a specific hub. In our case, we are saying with the Broadcast function is called on the server we want the client to update the BroadcastResults.Text with the value from the server.

Wrapping Up

Run the Web API and then UWP application and you will see the date time from the server being shown on the client. This is, of course, is the simplest of use cases for SignalR, but it is a great jumping off point to go deeper.

This tutorial from the official docs helped a lot when getting my sample up and running.

The code for this sample can be found here.

More OAuth

Last week I covered using OAuth with Google for authentication. I thought I would try GitHub next only to find out Microsoft does not have a built in provider. After a little searching I came across OWIN OAuth Providers on GitHub. This project has a ton for providers ranging from BattleNet to Yammer.

To install run the following from the package manager console.

Install-Package Owin.Security.Providers

The rest of the process is very similar to last weeks post. I am going to walk through GitHub for this example but the others are very similar. The big difference for each provider is getting API access.

At the top of Statup.Auth.cs in the App_Start folder add the following.

using Owin.Security.Providers.GitHub;

And at the bottom of the ConfigureAuth function.

app.UseGitHubAuthentication(
    clientId: "Your Client ID",
    clientSecret:"Your Client Secret");

The next setup is to set up API access with GitHub. Login to GitHub and go to Developer applications.

oauthgithub

Click the Register new application button in the upper right of the page. On the registration page enter an application name, homepage URL and authorization callback URL. As last week the URL options will be need to be based on your project settings. After entering all your information click Register application.

oauthgithubRegisterApplication

Then next page will show your client ID and client secret which just need to be entered in Statup.Auth.cs. That is all there is to adding an additional OAuth provider and give your users even more authentication options.

oauthgithubLogin

Using OWIN OAuth Providers makes adding access other OAuth providers just as simple as the providers from Microsoft.

OAuth 2.0 with Google

After getting Basic Authentication and Authorization working I thought it would be neat to add login via Google which involves using OAuth 2.0. To get started I used NuGet to update all the OWIN related packages in my project. I ran across some people who had issues with older versions of OWIN so I recommend getting the latest to avoid any potential problems.

Some third parties require SSL when using their auth services so I decided to go a head and change my application to use SSL. I hit a couple of issues during this part of the process that I want to point out. The first is that when you change to SSL IIS Express defaults to using port 44300. I missed this the first try and it took a good bit of searching before I spotted the problem. Even after reverting all my changes I could not get my site to load without errors. A reboot got my reverted site working which I am assuming was a configuration issue with IIS Express that got reset on reboot.

To enable SSL select the project in Solution Explorer and press F4 to bring up the properties window (which has different options that the project properties you get if you right-click the project and click properties). Set the SSL Enabled property to true. Copy the SSL URL to use when updating the project.ContactsPropertiesSsl

Back in the Solution Explorer right-click on the project and click properties. On the Web tab under the Servers section paste the SSL URL from above into the Project Url and save.ContactsProjectPropertiesSslUrl

When running the first time after enabling SSL Visual Studio will prompt asking if you would like to trust the self-signed certificate that IIS Express generated. I chose to trust in order to avoid warnings from the browser.

OAuthTrustIISSSL

The last change need in the project is in the Statup.Auth.cs found in the App_Start folder. In the ConfigureAuth function add the following code using your own ClientId and ClientSecret.

app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
    ClientId = "Your Client ID",
    ClientSecret = "Your Client Secret"
});

If you don’t have a client ID and client secret head to the Google Developers Console. The first step in the process is to Click the Create Project button.

GoogleDevelopersConsoleEnter a project name and project ID. The refresh button on the project ID field will randomly generate project IDs in case the one you want is already in use. When finished click Create.
GoogleDevelopersConsoleNewProjectAfter the project creation process finishes click on the name of your project. Next click on the APIs & auth section and then click the Consent screen option. This set of options determines what the user sees when Google prompts a user for consent to use information from their Google account. The minimum needed is email address and product name. For a live application I would recommend filling out as much as possible to give the user the best experience. When done click Save.GoogleDevelopersConsoleAPIsAndAuthConsentScreenIn the same APIs & auth section click the APIs option. Search for Google+ API. Click the name Google+ API and on the next screen click Enable API.GoogleDevelopersConsoleAPIsAndAuthApisGooglePlusAgain in the APIs & auth section click on Credentials option and then in the OAuth section fo the page click Create new Client ID.
GoogleDevelopersConsoleAPIsAndAuthCredentialsThe following dialog will show. Select the appropriate Application type which is Web application for this example. For Authorized JavaScript origins use the value from Project Url listed above which in my case is https://localhost:44300/. For Authorized redirect URIs the base URI is the same but with an added level. For MVC 5 the redirect should be set to https://localhost:44300/signin-google changing the base URI as needed of course. Click Create Client ID and you will be returned to the previous page that will now list the Client ID and Client Secret needed in Statup.Auth.cs.
GoogleDevelopersConsoleAPIsAndAuthCredentialsCreateClientId

Now the login page will have a button for Google which will allow users to create an account and associate it with their Google account. After the association users will be able to login with their Google account.LoginWithGoogle

 

From this point adding Microsoft, Facebook or Twitter would just be a matter of adding the desired options to ConfigureAuth Startup.Auth.cs and going to each service and requesting API access.

Basic Authentication and Authorization

When I created my contact application I did so with authentication set to Individual User Accounts as seen below.
vs2013NewAspProject

 

By choosing an authentication option during project creation Visual Studio takes care of a lot of the work involved with getting authentication and authorization up and running. For individual user accounts the default data store is SQL and Visual Studio sets up the need classes and data using entity framework to create a database and the needed tables to store authentication data. An AccountController and associated views are also created to handle user creation and login.

I am not going to cover everything that Visual Studio created automatically. I will be walking you through the other changes are needed to start using authentication and authorization with all the bits Visual Studio has provided.

The first step is to add AuthorizeAttribute to the FilterConfig which is found in the App_Start folder.

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute());
        filters.Add(new AuthorizeAttribute());
    }
}

Now that the AuthorizeAttribute is added to the applications filters any page that is requested by a user that is not authorized will be redirected to a login page to authenticate.

With authorization and authentication now in play any controller action that should allow anonymous usage will need to have the AllowAnonymous attribute added. The following example is using AllowAnonymous on the index action of the home controller.

public class HomeController : Controller
{
    [AllowAnonymous]
    public ActionResult Index()
    {
        return View();
    }
}

That is all there is to get the very basics going. This is a big subject, but Visual Studio and .Net do a lot of work to make getting basics up and going simple.

Adding Paging

As the number of contacts grows it is overwhelming to see in single page. To address this I am going to add paging to the contact list and only show 10 contacts per page.

To generate a large set of realistic date I am using Mockaroo. They offers data in CSV, Tab-Delimited, SQL, Excel, JSON and DBUnit XML with up to 1,000 rows of data per generation for free. The data Mockaroo is way better than the crazy stuff I was coming up with. If you need more than 1,000 rows at a time they do have a pay option.

For paging I am going to take advantage of the PagedList.Mvc NuGet package. Using the package manager console it can be installed with the Install-Package PagedList.Mvc command as seen in the following screenshotPackageManager-PagedListMvc.

To start using PagedList.Mvc I need the following changes to ContactsController. Add new using for PagedList and the index action needs a page parameter. As part of this change I also refactored the contact query to the GetContacts function so the Index action is much less cluttered than before. The function is now shown below but I moved the distinct city query to the GetDistinctCities function.

        
public ActionResult Index(string filterCity, string filterSearch, int? page)
{
    ViewBag.filterCity = filterCity;
    ViewBag.filterSearch = filterSearch;
    ViewBag.city = new SelectList(GetDistinctCities());

    var contacts = GetContacts(filterCity, 
                               filterSearch)
                   .ToPagedList(page ?? 1, 10);

    if (Request.IsAjaxRequest())
    {
        return PartialView("_ContactList", contacts);
    }

    return View(contacts);
}

private IQueryable<Contact> GetContacts(string city, string search)
{
    var contacts = from c in db.Contacts
                   select c;

    if (!string.IsNullOrWhiteSpace(search))
    {
        contacts = contacts.Where(c => c.Name.Contains(search));
    }

    if (!string.IsNullOrWhiteSpace(city))
    {
        contacts = contacts.Where(c => c.City == city);
    }
     return contacts.OrderBy(c => c.Name);
}

Notice that I am now saving the parameter values of the city and search filters to the ViewBag in the index action which will be used when changing pages when a filter is in play.

In GetContacts I had to add an order by statement to the contacts being returned. This is to make sure the list is always in the same order. It will also be a good spot to add different ordering options in the future.

The last change in the Index action was to take the return value from GetContacts and call ToPagedList on it. This function takes a page number and number of items per page and returns a paged list as the name implies. In my case if page is null I am using page 1 and each page will contain 10 items.

In both Index.cshtml and the partial view _ContactList.cshtml the models need to be changed to PagedList.IPagedList. In addition @Html.DisplayNameFor also needs to be changed from model.Name to model.FirstOrDefault().Name. The last step is to add the paged list control to the bottom of the view. Here is the code from the contact list partial view.

@using PagedList.Mvc
@model PagedList.IPagedList<Contacts.Models.Contact>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.FirstOrDefault().Name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.FirstOrDefault().Address)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.FirstOrDefault().City)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.FirstOrDefault().State)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.FirstOrDefault().ZipCode)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.FirstOrDefault().Country)
        </th>
        <th></th>
    </tr>

    @foreach (var item in Model)
    {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Address)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.City)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.State)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ZipCode)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Country)
            </td>
            <td>
                @Html.ActionLink("Edit", "Edit", new { id = item.Id }) |
                @Html.ActionLink("Details", "Details", new { id = item.Id }) |
                @Html.ActionLink("Delete", "Delete", new { id = item.Id })
            </td>
        </tr>
    }

</table>

@Html.PagedListPager(Model,
                     page => Url.Action("Index",
                                        "Contacts",
                                        new { ViewBag.filterCity,
                                              ViewBag.filterSearch,
                                              page }),
                     PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing(
                          new AjaxOptions
                          {
                              HttpMethod = "GET",
                              UpdateTargetId = "contactList"
                          }))

The Url.Action on the PagedListPager is the function that will be called when the user want to changes pages. Index is the action that will be called, Contacts is the controller that the action will be called on. Next is an anonymous type that will be processed to determine what parameters the index action will be called with. It is important that the existing filters are passed to the index action or else when the pager requests a different page it will return all contacts instead of next page of the filtered set of contacts.

The other nice option that the PagedListPager offers is the option to use ajax when requesting a different page. Since the contact page is using ajax when filtering it would be silly not to do the same when paging. To enable ajax just use the PagedListRenderOptions.EnableUnobtrusiveAjaxReplacing with the same AjaxOptions as the filter form.

As a side note I have changed the AjaxOptions on the index view that are used for filtering to match what is shown above for paging. I also removed the accepted verbs from the index action so it only responds to get requests. The request for a new set of data based on a filter change is really more of a get operation than a post operation.

Partial View with AJAX

This is step two of applying filters to my contact list without refreshing the whole page. I will be using the partial view created in last week’s post.

I ran into a bit of trouble getting ajax working. It seems that starting in MVC 5 the jQuery plugin needed to make this form of ajax work is no longer included by default. In order to fix use NuGet and install jQuery.Unobtrusive.Ajax.nuget-jqueryajax

After the NuGet package is installed add a new bundle (or add to an existing bundle) for the jQuery unobtrusive files to the BundleConfig found in the App_Start folder.

bundles.Add(new ScriptBundle("~/bundles/jqueryunob").Include(
            "~/Scripts/jquery.unobtrusive*"));

If a new bundle was added then make sure to render the new bundle in the _Layout.cshtml file in the Views/Shared folder.

@Scripts.Render("~/bundles/jqueryunob")

The next step was to change the index action of contacts controller. The index action now needs to accept both gets and posts which can be accomplished via the AcceptVerbs attribute. The second change needed in the index action is to return a partial view instead of a view if the request is an ajax request.

[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult Index(string city, string search)
{
    var cityList = new List<string>();
    var cityDistinct = from c in db.Contacts
                       orderby c.City
                       select c.City;

    cityList.AddRange(cityDistinct.Distinct());
    ViewBag.city = new SelectList(cityList);

    var contacts = from c in db.Contacts
                   select c;

    if (!string.IsNullOrWhiteSpace(search))
    {
        contacts = contacts.Where(c => c.Name.Contains(search));
    }

    if (!string.IsNullOrWhiteSpace(city))
    {
        contacts = contacts.Where(c => c.City == city);
    }

    if (Request.IsAjaxRequest())
    {
        return PartialView("_ContactList", contacts);
    }

    return View(contacts);
}

Reusing the index action is only one option to implement the needed changes. Another option would be to add another action to the controller that would return the partial view. In this case the  common query code would be moved to a function.

The last set of changes needed are in the index view. Instead of using Html.BeginForm Ajax.BeginForm should be used. The section of the page that will be replaced via the ajax request needs to be moved to a div with an id. In the example below I added a div with the id of contactList. It is important that the div id match the UpdateTargetId in AjaxOptions.

@model IEnumerable<Contacts.Models.Contact>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @using (Ajax.BeginForm("Index",
                           "Contacts",
                           new AjaxOptions
                           {
                               UpdateTargetId = "contactList"
                           }))
    {
        <p>
            City: @Html.DropDownList("city",
                                     ViewBag.city as SelectList,
                                     "All",
                                     new {@class = "city",
                                          onchange = "$(this.form).submit();"})
            Name: @Html.TextBox("Search")
            <input type="submit" value="Filter"/>
        </p>
    }
</p>
<div id="contactList">
    @{
        Html.RenderPartial("_ContactList");
    }
</div>

One thing to make special note of is the that the onchange for the city drop down list has change from “this.form.submit();” to “$(this.form).submit();”. Without this change the request will not come through as an ajax request and the full page will refresh instead of just the contact list section. I wasted a lot of time trying to track down why a full page request was happening and the issue ended up being the way that the drop down list was submitting the form.

Partial Views

On my contacts list page I want to apply filters without refreshing the whole page. This post is about step one of my first try at accomplishing this goal.

To add a partial view right clicked on the destination folder for the new view and from the Add menu select the View option.AddViewMenu

This brings up the Add View dialog. I am adding a view call _ContactList using the list template since this is going to be a list of contact. Contact is the model the list will be based on and any data access will happen via the ContactDbContext. The last thing to do is check the create as a partial view check box. Clicking add creates a _ContactList.cshtml file.AddViewDialog

The resulting code in _ConactList.cshtml looks like this:

@model IEnumerable<Contacts.Models.Contact>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Address)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.City)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.State)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.ZipCode)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Country)
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Address)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.City)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.State)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.ZipCode)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Country)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
            @Html.ActionLink("Details", "Details", new { id=item.Id }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.Id })
        </td>
    </tr>
}

</table>

As you can see by choosing the contact model Visual Studio was able to build a view that displays all the properties of the contact. Since I chose the list template the view is expecting a list of contacts that it will iterate over that list and create a table from the list of contacts.

In my index view I was able to remove all the code associated with the contact list and replace it with the following code which renders my new partial view.

@{
    Html.RenderPartial("_ContactList");
}

This partial view will allow me to have a consistent view of my contact list any where I may need it. It is also my hope that this partial view will allow me to refresh the list portion of the contact list page when a filter is changed.

Add a Dropdown Filter

On my contacts application I wanted the option to only show contacts from a specific city. To accomplish this I am going to use a dropdown with a distinct list of cities that exist in my contact list.

Below is part of the existing contacts controller with the Index action that gets all contacts and returns a view.

public class ContactsController : Controller
{
    private ContactDbContext db = new ContactDbContext();

    // GET: Contacts
    public ActionResult Index()
    {
        var contacts = from c in db.Contacts
                       select c;
        return View(contacts);
    }
}

Using ContactDbContext, which is a entity framework DbContext for contacts, the database will be queried for a list of distinct cities. The list of distinct cities is then stored as a select list in the view bag.

var cityList = new List<string>();
var cityDistinct = from c in db.Contacts
                   orderby c.City
                   select c.City;

cityList.AddRange(cityDistinct.Distinct());
ViewBag.city = new SelectList(cityList);

ViewBag is a dynamic object and is one way to pass data from a controller to a view. A dynamic object basically allows you to add properties just by setting them. In the above example the with ViewBag.city is adding a city property to the ViewBag and setting it equal to a new SelectList.

The resulting index action now takes a city parameter which is used when querying for contact if it has a value.

public ActionResult Index(string city)
{
    var cityList = new List<string>();
    var cityDistinct = from c in db.Contacts
                       orderby c.City
                       select c.City;

    cityList.AddRange(cityDistinct.Distinct());
    ViewBag.city = new SelectList(cityList);

    var contacts = from c in db.Contacts
                   select c;

    if (!string.IsNullOrWhiteSpace(city))
    {
        contacts = contacts.Where(c => c.City == city);
    }

    return View(contacts);
}

In my razor view the following adds the new city filter to the UI.

@using (Html.BeginForm("Index", "Contacts", FormMethod.Get))
{
<p>
    City: @Html.DropDownList("city", 
                             ViewBag.city as SelectList, 
                             "All", 
                             new {@class = "city", 
                                  onchange = "this.form.submit();"})
</p>
}

“All” is the label used for the default empty item. The last bit is for HTML attributes and set the class for the drop down to “city” and makes it so the form submits on change of the drop down. This means that when a new city is selected the view will refresh and only show contacts for the newly selected city.

This is the resulting UI:CityFilterUi