Quantcast
Channel: TYPE CAST EXCEPTION - ASP.Net
Viewing all 34 articles
Browse latest View live

Creating a Clean, Minimal-Footprint ASP.NET WebAPI Project with VS 2012 and ASP.NET MVC 4

$
0
0

Cutting the Bloat and Cruft Out of the Standard ASP.NET WebApi Project in Visual Studio

The default WebApi project template that ships with Visual Studio 2012 has a lot to offer in some respects, but is less than ideal for either learning your way through the impressive WebApi framework, or building a lean, mean API project with a minimal footprint.

The default template in VS 2012 creates a project with no less than 40 references alone, which include a whole lot of stuff you may not want or need for your Simple API project. Also, the project created includes a morass of script files, images, Nuget Packages, and other supporting items for a fully-functional MVC site, but not needed for all API projects. In fact, there is a good chance your project needs only a fraction of the total.

The Default WebApi Project is Cool, for Certain Cases, But Overkill for Many

Lest you think I am dumping on the ASP.NET team, I am not. Far from it. The default WebApi project comes pre-fabbed with literally everything you need to jump-start a large and complex Web Service/API project, including self-updating, user-facing API Documentation (that's the purpose of the full-formed MVC site that loads with the project). Just checking out the solution explorer tells a story about the size of project you are getting, when you consider that each of those folders is basically bulging with files:

standard-web-api-template-solution-explorer

However, if you want to start simple, there's a lot to wade through here, and it can make figuring out what's going on rather challenging. Here is how to fix that.

I. Get the Empty ASP.NET WebApi Project Template from Visual Studio Gallery

Nikos Baxevanis has authored a terrific installable template for WebApi projects which is really, really empty. It installs into VS and is available as an ASP.NET MVC 4 Template through the File/New Project Menu. Once you install the template into VS, you can create a very empty WebApi project. After we add a few helpful items back in, we still end up with a project with less than half the dependencies, and a small fraction of the files found in the standard VS WebApi template project.

  • Download the Installer HERE
  • Make sure all instances of Visual Studio are CLOSED
  • Run the installer

II. Create an Empty WebApi Project

Now, Open Visual Studio. Go to File/New Project, and select the ASP.NET MVC 4 Web Application Template:

file-new-webapi-standard

Then, once you name your project appropriately, and hit Ok, you will see the available templates for this project type. Notice, there is now an Empty WebApi Project template (this was not there before):

select-empty-web-api-project

Once you hit Ok, VS will create a project with far fewer references, and none of the extra stuff that came with the standard WebApi project:

empty-web-api-template-solution-explorer

At this point, we are almost, but not quite, done.

III. Making it Work: Update the Nuget Packages for the Empty WebApi Template

WebApi has been updated since the last time the Empty WebApi Template has been updated, so we need to update the packages used in our project. In the next steps, we will be adding some items to our project which depend on the most current packages for WebApi. in VS, go to the Tools Menu/Library Package Manager/Manage Packages for Solution:

EmptyWebApiTemplate.sln - Manage NuGet Packages

Update all of the packages listed.

IV. Making it Work: Add the Microsoft.AspNet.WebApi.Tracing Package (Optional)

ASP.NET WebApi projects commonly include tracing to make debugging a little less unpleasant, via the Microsoft.AspNet.WebApi.Tracingpackage. While not a requirement, you may consider adding this package via Nuget. Once again, open Tools Menu/LibraryPackage Manager/Manage Packages for Solution, then type WebApi.Tracing in the Search box. Select the Microsoft.AspNet.WebApi.Tracing package, and click Install:

WebApiTracing - Manage NuGet Packages

V. Making It Work: Adding WebApi Routing and Filters

Now that you have a more streamlined starting point, you will likely want to add a few things back in. In order to take advantage of the awesome that the MVC Team has created with WebApi (and to follow the MVC philosophy emphasizing convention over configuration), we will want some routing, and probably (but not necessarily) we will want to adhere to some MVC conventions in our project organization.

Add a new folder to the project, and name it App_Start (similar to that in the default MVC and/or WebApi Project template). Then right-click on the folder and select "Add Class" and name it  WebApiConfig.cs. Add the following code in place of the default code that VS creates in the class (note this is a static class). This class will set up the default routing convention similar to any other ASP.NET MVC Project. Note to replace the using statements at the top of the file as well as the code body:

Code for the WebApiConfig Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace EmptyWebApiProject.App_Start
{
publicstaticclass WebApiConfig
    {
publicconststring DEFAULT_ROUTE_NAME = "MyDefaultRoute";
publicstaticvoid Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                name: DEFAULT_ROUTE_NAME,
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
            config.EnableSystemDiagnosticsTracing();
        }
    }
}

Now, Add another class, and name it FilterConfig.cs. Copy the following code in place of the VS-generated code (as before, make sure to add the using statements as well as the code body):

Code for the FilterConfig Class:
using System.Web;
using System.Web.Mvc;
namespace EmptyWebApiProject.app_start
{
publicclass FilterConfig
    {
publicstaticvoid RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
        }
    }
}

 

VI. Making it Work: Update the Global.asax File to initialize Routes and Filters

Now that we have added the Routing and Filter files, we need to make sure we initialize them in our Global.asax file. Open that file and add the lines indicated to the existing code:

using System.Web;
using System.Web.Http;
using System.Web.Mvc;
//Add this Using Statement:
using EmptyWebApiTemplateProject.app_start;
namespace EmptyWebApiProject
{
// Note: For instructions on enabling IIS6 or IIS7 classic mode, 
// visit http://go.microsoft.com/?LinkId=9394801
publicclass WebApiApplication : HttpApplication
    {
protectedvoid Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
// Add these two lines to initialize Routes and Filters:
            WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        }
    }
}

 

VII. Add a Models Folder and a Controllers Folder to Maintain Convention

While not required, it makes sense to adhere to conventions established for ASP.NET/MVC by organizing the code in your project. In the case of an API project such as we are creating here, the Models will, as usual, represent our data. However, unlike a standard MVC project, the Controllers, in this case, will all inherit from System.Web.Http.ApiController instead of System.Web.Mvc.Controller. For the moment, add the folders as seen below, and we now have a greatly simplified WebApi project structure:

Solutions Explorer with Added Models and Controllers Folders:

added-models-and-controllers-folder

 

VIII. Set Appropriate Start Action and Note the Server Properties

Since we are building a minimal API service and not a web site, we may want to modify what happens when we run our project in Visual Studio. Unless (or until) you plan to have your API project display a home page, you might consider setting the runtime property setting such that VS does not try to open your site in a local browser when debugging. While the project will still run properly on whichever development server you have configured in Visual Studio, your default browser will try to open the page and show an error, which can be distracting.

In the Solution Explorer, double-click the project Properties folder. This will open the Project settings. Click on the "Web" tab. Your default web settings will probably look something like this:

Default VS Start Action Settings:

default-vs-web-project-settings 

Not the highlighted item. We don't want a page to open (unless you are going to add one to the project), so we want to change that. Select the, "Don't open a page . . ." option instead:

Modified VS Start Action Settings:

modified-vs-web-project-settings

While you are here, scroll the window down, and take note of the Server Settings. The default will usually be to use the Local IIS Web Server/IIS Express installed with Visual Studio. This is fine, but note the Project URL and specifically, the port setting. You will need to use this when consuming your API externally while debugging. Or, you may choose to switch to the Visual Studio Development Server and specify a port manually. In either case, when you call into your API Project from an external project while debugging you will need to use this as the root URL.

Default Server Settings for Local Debugging:

default-vs-port-settings

Clone this Project from Source or Download as a Zip File

You can clone this ready-to-build-out project as we've put it together here from my Github Repo, or download the Zip file. Beware, the repo is a little kludgy (especially if you navigate into the "packages" directory), as I included all of the Nuget Packages required so that the project will be fully functional out of the box.

Note that cloning this project will not give you the installed template we examine in the post above. But you can clone this project, renamed appropriately, and build it out to suit. Also, if you find ways to improve on it, shoot me a pull request. I am capable of missing important stuff, and doing dumb things!

Ready to Build it Out?

To this point we have examined how to install the empty WebApi template, and add the minimal additional items such as routing, filters, and a familiar folder structure needed to get an MVC-conformant API project ready to build. Next, we'll build out a basic example project using our streamlined WebApi Template. 

Next: Building a WebApi Example with the Minimal WebApi Project


Building Out a Clean, REST-ful WebApi Service with a Minimal WebApi Project

$
0
0

Build Out a Clean, Minimal-Footprint REST-ful API

In a previous post, we saw how to create a minimal ASP.NET WebApi project template, so that we can avoid some of the bloat and complexity inherent in the standard VS WebApi project. Getting started building out an API project using the minimal project template is much easier, and we can always add back stuff as our project grows.

For starters, though, the minimal project makes it much easier to focus on our principle task – creating a simple, clean API. This minimal project is especially useful if, like myself, you are less-than-experienced, and learning your way through Web API development. Or, if you just want a simple project.

The source code for the projects used in this article can be found at my Github Repo:

I. Create an Empty (or nearly so) WebApi Project


As we have seen, the default WebApi project in Visual Studio can be a bit much. To start with a stripped-down, minimal project template, either review the previous post referred to in the link above, which will install the Empty WebApi Project template into Visual Studio directly, clone the EmptyWebApi Project from my Github Repository, or download the zipped project. 

II. Modeling Our Data

In building our example, we will adhere to what are recommended conventions for an MVC project. While we are not creating a website proper, it will serve us well to follow the conventions designed into the MVC framework (of which WebApi is a part). Note how I have set up the basic project, with a Models folder and a Controllers folder (at this point, we don't need Views). If you have worked with a standard ASP.NET MVC project before, this part should look pretty familiar.

Right-click on the Models folder in Solution Explorer, and select the "Add Class" Menu Item. Name the class Person and hit Ok. Now add the following code to the Person class:

Code for the Person Class:
publicclass Person
{
publicint Id { get; set; }
publicstring FirstName { get; set; }
publicstring LastName { get; set; }
}

 

The Person class will be used to represent data from our data store once loaded into our application. Again following something of a convention, we will use a simple repository pattern to represent our database. It is good practice to use an interface to abstract away the specific implementation of your data store, and we will do so here. First of all, because we are going to avoid cluttering up this tutorial (at this point) with details of wiring up a database, and second of all because even in a finished project, your data storage requirements may change. With a properly implemented interface representing your datastore in code, changing up the database-specific code does not require changes to the rest of our project, only the concrete database implementation classes.

NOTE: I am aware that the Repository Pattern has fallen slightly out-of-favor in some quarters. Also, If you are planning to use Entity Framework, NHibernate, or another ORM, your approach in a real project might be different with respect to actual data access implementation. However, for this simple example, the repository pattern works.

Right-click on the Models folder again, and this time select "Add New Item." From from the "Code" sub-menu, select "Interface." Name the interface IPersonRepository and hit Ok. Then, add the following code to the newly created interface:

Code for the IPersonRepository Interface:
interface IPersonRepository
{
    IEnumerable<Person> GetAll();
    Person Get(int id);
    Person Add(Person person);
void Remove(int id);
bool Update(Person person);
}

 

Now that we have defined the interface for our repository, let's create a concrete implementation. Right-click once more on the Models folder in Solution Explorer, and select "Add Class" again. Name the new class PersonRepository and hit Ok. Now add the following code, which we'll walk through in a moment:

Code for the PersonRepository Class:
publicclass PersonRepository : IPersonRepository
{
// We are using the list and _fakeDatabaseID to represent what would
// most likely be a database of some sort, with an auto-incrementing ID field:
private List<Person> _people = new List<Person>();
privateint _fakeDatabaseID = 1;
public PersonRepository()
    {
// For the moment, we will load some sample data during initialization. 
this.Add(new Person { LastName = "Lennon", FirstName = "John" });
this.Add(new Person { LastName = "McCartney", FirstName = "Paul" });
this.Add(new Person { LastName = "Harrison", FirstName = "George" });
this.Add(new Person { LastName = "Starr", FirstName = "Ringo" });
    }
public IEnumerable<Person> GetAll()
    {
return _people;
    }
public Person Get(int id)
    {
return _people.Find(p => p.Id == id);
    }
public Person Add(Person person)
    {
if (person == null)
        {
thrownew ArgumentNullException("person");
        }
        person.Id = _fakeDatabaseID++;
        _people.Add(person);
return person;
    }
publicvoid Remove(int id)
    {
        _people.RemoveAll(p => p.Id == id);
    }
publicbool Update(Person person)
    {
if (person == null)
        {
thrownew ArgumentNullException("person");
        }
int index = _people.FindIndex(p => p.Id == person.Id);
if (index == -1)
        {
returnfalse;
        }
        _people.RemoveAt(index);
        _people.Add(person);
returntrue;
    }
}

 

Note in the code above, we are using a List<Person> to represent a datastore. In a real API application, you would most likely be using this class to access a database, XML file, or other persistence mechanism. We will look at connecting it all to a database in another post. For now I am keeping it simple, and loading some mock data into the list during initialization.

Using Api Controllers to Access Our API Service

Unlike an ASP.NET/MVC Project, which generally uses Controllers derived from System.Web.MVC.Controller, a WebApi project will generally utilize controllers derived from System.Web.Http.WebApiController. Like the standard MVC Controller, The WebApi Controller accepts incoming HTTP requests and processes accordingly. However, the standard MVC Controller returns a View, while the WebApi controller returns data.

Of particular note is that the WebApiController examines the Accept Header in the HTTP Request, and (where applicable) converts our .Net objects into the appropriate return data type (usually either XML or JSON). In this way, consumers of your API can specify the format required, and expect the proper return in the HTTP Response from the server. Content negotiation details are beyond the scope of this article, but suffice it to say that the WebApiController is what delivers the goods in a WebApiProject.

Right-click on the Controllers folder and select "Add Controller." In the dialog window, name the new controller PersonController and then select Empty WebApi Controller from the drop-down list of controller templates:

add-person-controller

Notice that the generated class inherits from ApiController.

Now add the following code for some basic CRUD operations to the new controller (Note you need to add the using statement to reference the classes you defined in the Models Folder – see comment at the top of the class):

Code for the PersonController Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
// Add a using statement to refer to Models:
using MinimalApiDemo.Models;
namespace MinimalApiDemo.Controllers
{
publicclass PersonController : ApiController
    {
staticreadonly IPersonRepository databasePlaceholder = new PersonRepository();
public IEnumerable<Person> GetAllPeople()
        {
return databasePlaceholder.GetAll();
        }
public Person GetPersonByID(int id)
        {
            Person person = databasePlaceholder.Get(id);
if (person == null)
            {
thrownew HttpResponseException(HttpStatusCode.NotFound);
            }
return person;
        }
public HttpResponseMessage PostPerson(Person person)
        {
            person = databasePlaceholder.Add(person);
string apiName = App_Start.WebApiConfig.DEFAULT_ROUTE_NAME;
            var response = this.Request.CreateResponse<Person>(HttpStatusCode.Created, person);
string uri = Url.Link(apiName, new { id = person.Id });
            response.Headers.Location = new Uri(uri);
return response;
        }
publicbool PutPerson(Person person)
        {
if (!databasePlaceholder.Update(person))
            {
thrownew HttpResponseException(HttpStatusCode.NotFound);
            }
returntrue;
        }
publicvoid DeletePerson(int id)
        {
            Person person = databasePlaceholder.Get(id);
if (person == null)
            {
thrownew HttpResponseException(HttpStatusCode.NotFound);
            }
            databasePlaceholder.Remove(id);
        }
    }
}

 

A Closer Look at the WebApi Controller

As we have alluded to previously, a core tenet of MVC is to favor Convention over Configuration. In terms of our controller class, what this means is that the ASP.NET/MVC runtime establishes certain default behaviors which require no additional configuration, unless you want to change them. Initially, for me anyway, this was also a source of confusion, as the runtime appeared to perform sufficient "magic" that I didn't know what was happening.

One of the MVC conventions is the mapping of controller methods to HTTP verbs. In our case, we are specifically mapping methods to the HTTP verbs GET, POST, PUT, and DELETE. Notice how our method names begin with one of these four verbs? Our two methods which retrieve person data, GetAllPeople and GetPersonByID begin with the word "Get." The MVC runtime will map HTTP GET requests to this controller and to one of these methods by following the convention set up in the Route mapping we registered in our WebApiConfig file:

If we go into the App_Start folder, and open the WebApiConfig file, we should see something similar to this:

publicstaticclass WebApiConfig
{
publicconststring DEFAULT_ROUTE_NAME = "MyDefaultRoute";
publicstaticvoid Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: DEFAULT_ROUTE_NAME,
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
        config.EnableSystemDiagnosticsTracing();
    }
}

 

In the above, the route template is used by the MVC framework to map incoming HTTP requests to the appropriately named controller (named such that the first part of the controller name matches the {controller} mapping, and to the most appropriate method with a name that begins with the HTTP verb specified in the request.

In order for all this to work, it becomes our responsibility to simply name our controllers and methods in accordance with this convention. For example, a GET request coming in with a URL mapping:

http://localhost:<port number>/person/

will be mapped to the PersonController.GetAllPeople() method, since the URL contains the Person mapping, the request contains the GET verb, and the optional /id parameter is not present.

Similarly, an HTTP GET request with the following URL mapping:

http://localhost:<port number>/person/2

will be mapped again to the PersonController, but this time to the GetPersonByID method, since the optional id parameter is provided.

Consuming The Example API with a Simple Console Client

The value of an exposed API service is that independent client applications can consume and interact with our API data in a manner we control through the API. We can make available as much, or as little, access as we wish. To demonstrate, we will create a very simple console application which will exercise our API project in a couple different ways.

To get started, open a second instance of Visual Studio, and use File –> New Project to create a Console Application. Then, go straight to the Tools –> Library Package Manager –> Manage Nuget Packages for Solution. Select "Online" in the left-hand tree menu, then, in the Search box, type "WebApi." Select the ASP.NET Web API Core Libraries package, and click Install.

Install WebApi Core Libraries in the Console Application

client-demo-install-webapi-core

Once the installation is complete, select the Updates item from the left-hand tree menu. Install any relevant WebApi library updates.

WebApi and JSON give us Options for Client Code

WebApi provides flexible options for how we might retrieve data from our API application. Right out of the box, the WebApi library provides built-in serialization and de-serialization for POCO ("Plain Old CLR Objects") objects we might use, as well as send and receive straight JSON ("JavaScript Object Notation") or XML.

We will take a (very!) rudimentary look at client code that uses primarily JSON. One might at some point call into an API infrequently, and deem it not worth building a class hierarchy for these infrequent calls. We will also look at a more standard .NET approach, in which class models are built which represent data from the API we are consuming.

The example client project we are about to build by no means represents a design we might build in the real world. The purpose is to take a quick look at calling into a simple API and doing something semi-meaningful with the data.

IMPORTANT NOTE: for this sample code, I am using calls to GetAsync and ReadAsAsync and accessing the Result property of the return value. This creates blocking call, which is less than optimal for most real-world projects. I used this call to keep things simple in this demo. However, we will look at making non-blocking asynchronous calls in upcoming post.

Building the JSON-Based Example Code

The following example does not rely on a Person class being present. We will use the Newtonsoft JSON library installed with WebApi to parse and use JSON, and to basically shuttle our data around. Where needed, we will utilize C# Anonymous classes where the API we are calling into demands it.

In the example project, I basically added a separate class for the separate calls into our API project. Then, I added an ugly, monolithic method which walks through each, and writes the data retrieved out to the console window. The important part in the code below is getting a feel for making the API calls.

I added a class called JsonExamples for this. At the top of the class, make sure to add the following using statements:

Add Using Statements to the JsonExamples Class:
using System;
using System.Net.Http;
using Newtonsoft.Json.Linq;

 

Then, separate methods to call into our API project:

The Calls to the API GET, POST, PUT, and DELETE Controllers Using JSON:
// Sends HTTP GET to Person Controller on API:
static JArray getAllPeople()
{
    HttpClient client = new HttpClient();
    HttpResponseMessage response = 
        client.GetAsync("http://localhost:57772/api/person/").Result;
return response.Content.ReadAsAsync<JArray>().Result;
}
// Sends HTTP GET to Person Controller on API with ID:
static JObject getPerson(int id)
{
    HttpClient client = new HttpClient();
    HttpResponseMessage response = 
        client.GetAsync("http://localhost:57772/api/person/" + id).Result;
return response.Content.ReadAsAsync<JObject>().Result;
}
// Sends HTTP POST to Person Controller on API with Anonymous Object:
static JObject AddPerson(string newLastName, string newFirstName)
{
// Initialize an anonymous object representing a new Person record:
    var newPerson = new { LastName = newLastName, FirstName = newFirstName };
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri("http://localhost:57772/");
    var response = client.PostAsJsonAsync("api/person", newPerson).Result;
return response.Content.ReadAsAsync<JObject>().Result;
}
// Sends HTTP PUT to Person Controller on API with Anonymous Object:
staticbool UpdatePerson(int personId, string newLastName, string newFirstName)
{
// Initialize an anonymous object representing a the modified Person record:
    var newPerson = new { id = personId, LastName = newLastName, FirstName = newFirstName };
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri("http://localhost:57772/");
    var response = client.PutAsJsonAsync("api/person/", newPerson).Result;
return response.Content.ReadAsAsync<bool>().Result;
}
// Sends HTTP DELETE to Person Controller on API with Id Parameter:
staticvoid DeletePerson(int id)
{
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri("http://localhost:57772/");
    var relativeUri = "api/person/" + id.ToString();
    var response = client.DeleteAsync(relativeUri).Result;
    client.Dispose();
}

 

Last, an ugly, procedural chunk of code that walks through each of the methods above and writes the results to the console window:

Ugly, Monolothic Console Output Code:
publicstatic void PrintJsonExamples()
{
    // WRITE ALL PEOPLE TO CONSOLE (JSON):
    Console.WriteLine("Retreive All The People:");
    JArray people = getAllPeople();
    foreach (var person in people)
    {
        Console.WriteLine(person);
    }
    // WRITE A SPECIFIC PERSON TO CONSOLE (JSON):
    Console.WriteLine(Environment.NewLine + "Retreive a Person by ID:");
    JObject singlePerson = getPerson(2);
    Console.WriteLine(singlePerson);
    // ADD NEW PERSON, THEN WRITE TO CONSOLE (JSON):
    Console.WriteLine(Environment.NewLine + "Add a new Person and return the new object:");
    JObject newPerson = AddPerson("Atten", "John");
    Console.WriteLine(newPerson);
    // UPDATE AN EXISTING PERSON, THEN WRITE TO CONSOLE (JSON):
    Console.WriteLine(Environment.NewLine + "Update an existing Person and return a boolean:");
    // Pretend we already had a person's data:
    JObject personToUpdate = getPerson(2);
string newLastName = "Richards";
    Console.WriteLine("Update Last Name of " + personToUpdate + "to " + newLastName);
    // Pretend we don't already know the Id:
    int id = personToUpdate.Value<int>("Id");
string FirstName = personToUpdate.Value<string>("FirstName");
string LastName = personToUpdate.Value<string>("LastName");
if (UpdatePerson(id, newLastName, FirstName))
    {
        Console.WriteLine(Environment.NewLine + "Updated person:");
        Console.WriteLine(getPerson(id));
    }
    // DELETE AN EXISTING PERSON BY ID:
    Console.WriteLine(Environment.NewLine + "Delete person object:");
    JsonExamples.DeletePerson(5);
    // WRITE THE UPDATED LIST TO THE CONSOLE:
    {
        // WRITE ALL PEOPLE TO CONSOLE
        Console.WriteLine("Retreive All The People using classes:");
        people = JsonExamples.getAllPeople();
        foreach (var person in people)
        {
            Console.WriteLine(person);
        }
    }
    Console.Read();
}

 

If we run the application by calling the PrintJsonExamples method, we see the JSON results in our console:

Console Output – Json Data:

json-console-output

Building the Class-Based Example Code

Depending on the needs of your project, you may decide adding a Person class to your API client makes sense. WebApi understands how to serialize and de-serialize .NET classes to and from JSON or XML, which are the two most common data exchange formats in use.

We can re-write our client examples to utilize a Person class if it suits our needs, and pass instances of Person to and from our API project indirectly through serialization. Our core client methods, re-written, look like this:

IMPORTANT: See the note above about the blocking calls used in this example!

The Calls to the API GET, POST, PUT, and DELETE Controllers Using an added Person Class:
// DEFINE A PERSON CLASS IDENTICAL TO THE ONE IN THE API:
publicclass Person
{
public int Id { get; set; }
publicstring LastName { get; set; }
publicstring FirstName { get; set; }
}
static IEnumerable<Person> getAllPeople()
{
    HttpClient client = new HttpClient();
    HttpResponseMessage response = 
        client.GetAsync("http://localhost:57772/api/person/").Result;
    client.Dispose();
return response.Content.ReadAsAsync<IEnumerable<Person>>().Result;
}
static Person getPerson(int id)
{
    HttpClient client = new HttpClient();
    HttpResponseMessage response = 
        client.GetAsync("http://localhost:57772/api/person/" + id).Result;
    client.Dispose();
return response.Content.ReadAsAsync<Person>().Result;
}
static Person AddPerson(Person newPerson)
{
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri("http://localhost:57772/");
    var response = client.PostAsJsonAsync("api/person", newPerson).Result;
    client.Dispose();
return response.Content.ReadAsAsync<Person>().Result;
}
static bool UpdatePerson(Person newPerson)
{
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri("http://localhost:57772/");
    var response = client.PutAsJsonAsync("api/person/", newPerson).Result;
    client.Dispose();
return response.Content.ReadAsAsync<bool>().Result;
}
static void DeletePerson(int id)
{
    HttpClient client = new HttpClient();
    client.BaseAddress = new Uri("http://localhost:57772/");
    var relativeUri = "api/person/" + id.ToString();
    var response = client.DeleteAsync(relativeUri).Result;
    client.Dispose();
}

 

Next, some minor variations on the ugly console-output code:

Ugly, Monolithic Code, Modified:
publicstatic void PrintClassExamples()
{
string personOutputString = "id: {0};  LastName: {1}, FirstName: {2}";
    // WRITE ALL PEOPLE TO CONSOLE
    Console.WriteLine("Retreive All The People using classes:");
    IEnumerable<Person> people = ClassBasedExamples.getAllPeople();
    foreach (var person in people)
    {
        Console.WriteLine(personOutputString, person.Id, person.LastName, person.FirstName);
    }
    // WRITE A SPECIFIC PERSON TO CONSOLE:
    Console.WriteLine(Environment.NewLine 
        + "Retreive a Person object by ID:");
    Person singlePerson = ClassBasedExamples.getPerson(2);
    Console.WriteLine(personOutputString, singlePerson.Id, 
        singlePerson.LastName, singlePerson.FirstName);
    // ADD NEW PERSON, THEN WRITE TO CONSOLE:
    Console.WriteLine(Environment.NewLine 
        + "Add a new Person object and return the new object:");
    Person newPerson = new Person { LastName = "Atten", FirstName = "John" };
    newPerson = AddPerson(newPerson);
    Console.WriteLine(personOutputString, newPerson.Id, 
        newPerson.LastName, newPerson.FirstName);
    // UPDATE AN EXISTING PERSON, THEN WRITE TO CONSOLE:
    Console.WriteLine(Environment.NewLine 
        + "Update an existing Person object:");
    // Pretend we already had a person's data:
    Person personToUpdate = getPerson(2);
string newLastName = "Richards";
    Console.WriteLine("Updating Last Name of " 
        + personToUpdate.LastName + " to " + newLastName);
    personToUpdate.LastName = newLastName;
if (ClassBasedExamples.UpdatePerson(personToUpdate))
    {
        Console.WriteLine(Environment.NewLine + "Updated person object:");
        Person updatedPerson = getPerson(2);
        Console.WriteLine(personOutputString, updatedPerson.Id, 
            updatedPerson.LastName, updatedPerson.FirstName);
    }
    // DELETE AN EXISTING PERSON BY ID:
    Console.WriteLine(Environment.NewLine + "Delete person object:");
    ClassBasedExamples.DeletePerson(5);
    // WRITE THE UPDATED LIST TO THE CONSOLE:
    {
        Console.WriteLine("Retreive All The People using classes:");
        people = ClassBasedExamples.getAllPeople();
        foreach (var person in people)
        {
            Console.WriteLine(personOutputString, person.Id, 
                person.LastName, person.FirstName);
        }
    }
    Console.Read();
}

 

NOTE: The data in the API project is not persisted. However, until the IIS Server re-starts, the data is maintained between project debugging sessions. Re-start IIS Express by right-clicking the IIS Express icon in the system tray. Then stop and restart the API project.

Again, if we run our code, we will see the console output, this time reflecting the manner in which we decided to format our class data:

Console Output – Class-Based Data:

class-based-console-output

Wrapping Up

We've taken a look at building out a very basic API using the minimal components. We've also looked quickly at creating a basic client to consume such an API, independently of the API project itself. Next we will examine implementing proper asynchronous methods in an API client, and examine more advanced controllers and querying.

Routing Basics in ASP.NET MVC

$
0
0

I5-and-I405-by-andrew-morton-500When getting started with ASP.NET MVC and/or the ASP.NET Web API, it can be overwhelming trying to figure out how it all works. These frameworks offer powerful features, and abstract away a good deal of pain associated with handling, routing, and responding to HTTP requests within an application. This is a great thing for seasoned developers who understand what it is the framework is doing "for" you (and how to modify that behavior, if desired). It also makes it easier for new or less-experienced folk to set up a basic site or API and watch it "just work."

On the other hand, the abstraction can make it challenging for those new to the MVC world to understand just what is going on, and where the critical functionality they want to modify "lives."

One of the fundamental concepts to understand when using ASP.NET MVC and/or the ASP.NET Web API is routing, which essentially defines how your application will process and respond to incoming HTTP requests.

Image by Andrew Morton

IMPORANT NOTE: This post covers the most basic and fundamental concepts of routing as applied to the ASP.NET MVC framework. The target audience are those with little or no familiarity with routing in general, or who may be looking to review the fundamentals. If you are an experience MVC developer, there is probably not much here for you, although your feedback in the comments is greatly appreciated.

In my next post, having covered the fundamentals, I will examine route customization. I will add a link here when that post is complete.

Routing Makes it All Work

Traditional web communication architecture maps a URL (Uniform Resource Locator) to a file within the file system. For example, the following:

http://mydomain.com/mybooks/favorites.html

 

would tend to map to a file named favorites.html, in the directory ~/mybooks/favorites, located in the root directory for the site mydomain.com. In response to an incoming HTTP request for this resource, the contents of the file are either returned (as in the example above, as HTML) or perhaps code associated with a file is executed (if, for example, the file were a .aspx file).

Within the MVC framework, as well as the Web API*, URLs are instead mapped to specific methods which execute in response to the incoming request, generally returning either a View (MVC) or some sort of structured data (Web API) corresponding to the the requested resource. In other words, instead of pointing to actual physical resources within a file system, MVC and Web API routes instead point to an abstraction which represents the resource requested, in both cases a method which will return the requested item.

NOTE: There are some subtle differences between MVC and Web API with respect to routing, but most of the concepts we cover here are mutually applicable. I attempt to clarify Web Api Routing Specifics in a separate post.

This de-coupling of the URL from the physical file system allows us to construct cleaner, more friendly URLs which are more beneficial to the user, search-engine-friendly, and (in theory) more persistent, meaning URLs associated with specific content are less likely to change, and break incoming links. In the authoritative book Professional ASP.NET MVC 4 , the authors refer to some common guidelines for high-quality URLs:

Usability expert Jacob Nielsen (www.useit.com) urges developers to pay attention to URLs and provides the following guidelines for high-quality URLs. You should provide:

  • A domain name that is easy to remember and easy to spell
  • Short URLs
  • Easy-to-type URLs
  • URLs that reflect the site structure
  • URLs that are hackable to allow users to move to higher levels of the information architecture by hacking off the end of the URL
  • Persistent URLs which don't change

* Technically, routing is incorporated into ASP.NET generally, and is available to all types of ASP.NET applications. However, the concept has been largely associated with MVC. Further, Web API actually contains its own implementation of routing, such the a Web API application can be hosted independently of ASP.NET and IIS.

How Routing Works in ASP.NET MVC

In MVC, the convention is to map URLs to a particular action (a method) on a particular controller. The action then executes, and (usually, but not always) returns an instance of ActionResult. The ActionResult class handles Framework logic such as rendering to HTML or JSON, and writing to the HTTP response that will be returned to the user's browser.

Once again, I defer to the authors of ASP.NET MVC 4 (who happen to also be members of the ASP.NET team):

"Routing within the ASP.NET MVC framework serves two main purposes:

  • It matches incoming requests that would not otherwise match a file on the file system and maps the requests to a controller action.
  • It constructs outgoing URLs that correspond to controller actions

The most basic version of this convention would be a URL as follows:

http://mydomain/controllername/methodname

 

In an MVC project, this is achieved by registering route templates which establish how incoming URLs will be mapped to specific controllers and actions. A typical MVC project defines a Global.asx file, which contains a single method – Application_Start. Within this method, calls are made to various configuration methods to set up the application's working state. One of these calls is to the RegisterRoutes method of the RouteConfig class found in the App_Start folder of the project.

Global.asx File and the RouteConfig File in a Typical MVC Project:

global-asx-and-route-config-files

 

If we examine the Global.asx file, we find the following code:

The Default ASP.NET MVC Global.asx File:
publicclass MvcApplication : System.Web.HttpApplication
{
protectedvoid Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}

 

For our purposes, we are interested only in the call to RouteConfig.RegisterRoutes. As we can see, the call passes the the Routes collection of the Global RouteTable as a parameter to the RegisterRoutes method, which then populates the routes collection with pre-defined route templates for the application. The default MVC project template comes with a single pre-configured route:

The Default MVC RouteConfig Class:
publicclass RouteConfig
{
publicstaticvoid RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new
            { 
                controller = "Home", 
                action = "Index", 
                id = UrlParameter.Optional
            }
        );
    }
}

 

Note that any MVC application must have at least one route definition in order to function. In the above, a route template named "Default" is added to the routes collection. The items in curly braces enclose Route Parameters, and are represented by the parameter name as a placeholder between the curly braces. Route Segments are separated by forward slashes (much like a standard URL). Notice how the implied relative URL our route specifies matches the MVC convention:

~/{controller}/{action}

 

Route parameters can be named just about anything, however ASP.NET recognizes a few special route parameter names, in particular {controller} and {action} , and treats them differently than other route parameters.

Controller Matching

When the routing framework encounters a route parameter named {controller}, it appends the suffix "Controller" to the value of the parameter, and then scans the project for a class by that name which also implements the System.Web.Mvc.IController interface. Note that the search for a controller with a matching name is case-insensitive.

Action Matching

Once the framework has selected the proper controller, it attempts to locate an action on the controller with a name matching the {action} parameter value. The search for a matching action name is case-insensitive. If more than one action matches by name (as with multiple overloaded methods on the same controller), the framework will select the method for which the most URL parameters match method arguments by name.

Action Parameter Matching

Additional URL Parameters other than {controller} and {action} are available to be passed as arguments to the selected Action method. The framework will evaluate the input arguments of the available actions, and match them by name (case-insensitively) to the URL parameters other than {action} and {controller} . With certain restrictions, the framework will select that action with the greatest number of matching parameters.

Some things to consider:

  • The MVC framework will first match method arguments by name to URL parameters. Then, it will attempt to match any query string parameters included in the URL by name. If the request is a POST, then the framework will attempt to match the contents of the POST body.
  • Method arguments are evaluated for a match by name only. The framework does not consider the type required by the method argument. For example, a URL parameter named {id} with a value of "John" will be considered a match for a method which accepts an int argument named id.
  • Action methods can be decorated with attributes which restrict the type of HTTP request they will respond to. Such attributes indicate the applicable HTTP verb to which the action will respond.

As an example of limiting the HTTP actions which a method may respond, consider the following:

Overloaded Action Method with HttpPost Attribute:
public ActionResult Edit(int id)
{
return View();
}
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
try
    {
// TODO: Add update logic here
return RedirectToAction("Index");
    }
catch
    {
return View();
    }
}

 

In the above, we find two methods named Edit. The first accepts an int named id as an argument, and the second accepts an int named id and a FormCollection (a complex type). The purpose of this overloaded method is so that a browser can:

  • Request a view with which to edit a record of some sort and then,
  • Submit the modified record values back to the site for storage.

The first Edit method, which requires only an intid argument will be called using HTTP GET, and return a view with the current representation of the data to be edited. Once the user has updated values in the view and submits the form data, an HTTP POST request is issued. The overloaded Edit method, decorated with the [HttpPost] attribute, is executed, and the modified data is persisted or otherwise processed. 

The MVC Default Route Template – Routing Walk-Thru

The route mapping assumes that the URL template specified is relative to the domain root for your site. In other words, since the entire application is hosted at http://yourdomain.com it is not necessary to include this domain root as part of the route template.

In the case of the default MVC mapping from our RouteConfig class above, the route contains the two special parameters, {controller} and {action}. In processing incoming requests, the framework appends "Controller" to the value provided for the {controller} parameter, and then searches the project for a controller class of that name. Once the proper controller has been identified, MVC next looks for a method name corresponding to the value of the {action} parameter, and then attempts to match any additional parameters with input arguments accepted by that method.

For example, if our application receives a request with the following URL:

http://mybookstore.com/books/details/25

 

the routing will match the default template. The string "Controller" will be appended to the "books" segment, and the runtime will set about searching the project for a class named BooksController. If the controller is located, MVC will then examine the controller for a public method named Details. If a Details method is found, MVC will attempt to find an overload which accepts a single argument named id, and then calls that method, passing in the final URL segment ("25" in this case) as an argument.

The following controller example would provide a suitable match for our incoming request:

A Simple Books Controller:
publicclass BooksController : Controller
{
public ActionResult Index()
    {
return View();
    }
public ActionResult Details(int id)
    {
return View();
    }
}

 

The incoming request would result in a call to the Details method, passing in the integer 25as the proper id argument. The method would return the appropriate view (how MVC does this is another convention for another post – let's stay focused on request routing for now).

Route Parameter Defaults

Notice in the RegisterRoutes method, the registration of the "Default" route also appears to assign some default values to the controller and action, parameters. These values will be used for any of these parameters if they are missing from the incoming request URL. Additionally, the id parameter is designated as optional. For example, consider the following URL:

http://mybookstore.com/books/

In this case, we have specified the Bookscontroller, but have not specified a value for the action or id. However, this route still matches our route definition, since MVC will provide the default value specified for the action parameter (in this case, index). Since the id parameter has been made optional in our route template, MVC will again search for a controller named BooksController, but in this case, examine the controller for a method named Index which does not require an argument. Again, a match is found, and the Index method is called, returning an appropriate view (most likely a list of all the books in the database).

The MVC default route mapping also specifies a default controller to use when no controller parameter is specified; namely, the "Home" controller. In other words, incoming requests to our domain root:

http://mybookstore.com/

will also match the default project controller. In this case, the runtime will attempt to locate a controller named HomeController, then locate the Index method of that controller. Since no id parameter was provided, the Index method will be called, returning the appropriate view ( most likely, the Homepage of our site).

What Next?

As we have seen above, MVC examines an incoming URL and attempts to map each URL segment to a controller and action according to the route templates set up in the RouteConfig.MapRoutes method. Once a proper controller and action have been identified, any additional URL segments (for example, the optional {id} segment in our example above) are evaluated against the action method signature to determine the best parameter match for the action.

But what happens when we need to do more than just send an ID in as an argument for the desired action method? Or, what if we have one or more overloaded methods by which we wish to perform more complex queries against our data?

While we can always include query parameters as part of our URL (and in fact we will no doubt have to resort to this at various points in our application design), we can customize and extend the default routing, and exert a little more control over how how and what our application will accept in an HTTP request by customizing our routes.

While the default /controller/action/id route baked into the MVC project template is a useful start and will handle many common controller cases, it is safe to say the MVC team did not expect developers to limit their applications to this minimally-flexible, single standard. Indeed, the ASP.NET routing framework (and the corresponding routing framework used by Web API) are very flexible, and within certain limits, highly customizable.

We'll look at route customization in the next post.

Additional Resources

 

Routing Basics in ASP.NET Web API

$
0
0

I210-and-I57-interchange-by-Rob-Reiring-320

As noted in Routing Basics in ASP.NET MVC, routing in ASP.NET Web API is functionally very similar to the standard MVC routing. Understanding the MVC routing model will provide most of what you need to know to leverage that knowledge against the Web API framework, so long as you keep in mind the key differences between the two.

The differences between the two are largely dictated by the different consumption models driving each type of application.

Image by Ron Reiring

IMPORANT NOTE: This post covers the most basic and fundamental concepts of routing as applied to the ASP.NET Web API framework. The target audience are those with little or no familiarity with routing in general, or who may be looking to understand the most basic differences between routing in ASP.NET MVC and Web API.

If you have little to no knowledge of MVC routing, you may want to review my post on MVC routing basics first.

In my next post, having covered the fundamentals, I will examine route customization. I will add a link here when that post is complete.

Web API and MVC – Two Separate Frameworks

Because the Web API grew (in part) out of the efforts of the ASP.NET MVC team, and because the default Web API project template in Visual Studio is bundled up inside an MVC application, it is easy to think that the Web API framework is some kind of subset of the MVC framework. In fact, the two inter-operate together very well, but that is by design. They are, in fact, separate frameworks which adhere to some common architectural patterns.

Instead of paraphrasing the overview of Web API and the architectural goals of the project, I will let the MVC team do so, because honestly, they know better than I do! from the most helpful book Professional ASP.NET MVC 4 authored by (mostly) the MVC team:

"ASP.NET MVC excels at accepting form data and generating HTML; ASP.NET Web API excels at accepting and generating structured data like JSON and XML." MVC has flirted with providing structured data support (with JsonResult and the JSON value provider), but it still fell short in several ways that are important to API programmers, including:

  • Dispatching to actions based on HTTP verbs rather than action names
  • Accepting and generating content which may not necessarily be object oriented (not only XML, but also content like images, PDF files, or VCARDs
  • Content type negotiation, which allows the developer to both accept and genereate structured content independent of its wire representation
  • Hosting outside of the ASP.NET runtime stack and IIS web server, something which WCF has been unable to do for years

The Web API team went to great lengths to try to allow you to leverage your existing ASP.NET experience with controllers, actions, filters, model binders, dependency injection, and the like. Many of these same concepts appear in Web API in very similar forms, which make applications that combine MVC and Web API seem very well integrated."

As noted above, one of the key differences between MVC and Web API is that Web API returns an appropriate representation of the model object as a result. A related difference is that Web API performs content type negotiation in response to incoming requests, and attempts to return the proper content type (JSON in response to an incoming request specifying JSON as the preferred content resource representation, for example).

Suffice it to say that Web API is an effort to create a mechanism for the efficient exchange of data and other content using HTTP. This effort is reflected in the architecture of the framework, and results in some of the differences in routing between the two frameworks.

Web API Route Configuration Files

The default Visual Studio Web API project template is rather large, as it incorporates a complete MVC project as well. The resulting project has some pretty cool features, including automatic documentation for the API you build (that is the purpose of the MVC site contained in the project), but also presents a rather bloated project file, with a large number of folders and dependencies.

NOTE: To see how to create a stripped-down Web API project template in Visual Studio, see Creating a Clean, Minimal-Footprint ASP.NET WebAPI Project with VS 2012 and ASP.NET MVC 4

As with a standard MVC project, the route configuration is called from the Global.asx file. However, in this standard Web API project, there exists both a RouteConfig class, and a WebApiConfig class, both in the Application_Start folder:

Global.asx File and the WebApiConfig File in a Typical MVC Project:

StandardWebApiTemplateProject

The RouteConfig.cs file here is the same as in any MVC project, and sets up routes for the MVC framework. The WebApiConfig.cs file is where our Web API routing configuration happens. However, if we open the file, it looks pretty familiar:

The WebApiConfig Class and MapHttpRoute Method:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace StandardWebApiTemplateProject
{
publicstaticclass WebApiConfig
    {
publicstaticvoid Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

There are a few important differences here, though. First, notice that in the using statements at the top of the file, there are no references to System.Web.Mvc but instead, we find a reference to System.Web.Http. That's because the Web API team re-created the routing functionality in a library with no dependencies on ASP.NET or MVC. Also notice that instead of calling Routes.MapRoutes as in the MVC RouteConfig class, we instead call Config.Routes.MapHttpRoutes.

And, as we can see, the default route template for Web API looks a little different.

How Routing Works for Web API

Unlike ASP.NET MVC, the Web API routing convention routes incoming requests to a specific controller, but by default simply matches the HTTP verb of the request to an action method whose name begins with that verb. Recall the default MVC route configuration:

The Default MVC Route Template:
{controller}/{action}/{id}

 

In contrast, the default Web API route template looks like this:

The Default Web API Route Template:
api/{controller}/{id}

 

The literal api at the beginning of the Web API route template above makes it distinct from the standard MVC route. Also, it is a good convention to include as part of an API route a segment that lets the consumer know they are accessing an API instead of a standard site.

Also notice that unlike the familiar MVC route template, the Web API template does not specify an {action} route parameter. This is because, as we mentioned earlier, the Web API framework will by default map incoming requests to the appropriate action based upon the HTTP verb of the request.

Consider the following typical Web API Controller:

WebApi Controller from Default Visual Studio WebApi Template
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace StandardWebApiTemplateProject.Controllers
{
publicclass ValuesController : ApiController
    {
// GET api/values
public IEnumerable<string> Get()
        {
returnnewstring[] { "value1", "value2" };
        }
// GET api/values/5
publicstring Get(int id)
        {
return"value";
        }
// POST api/values
publicvoid Post([FromBody]stringvalue)
        {
        }
// PUT api/values/5
publicvoid Put(int id, [FromBody]stringvalue)
        {
        }
// DELETE api/values/5
publicvoid Delete(int id)
        {
        }
    }
}

 

First, take note that Web API controllers do not inherit from System.Web.Mvc.Controller, but instead from System.Web.Http.Controller. Again, we are working with a familiar, but distinct library.

Of particular note, however, are the default method names (the example above is what is created as part of the default VS 2012 project template). Again, without specifying a specific method using an {action} route parameter, the Web API framework determines the appropriate route based on the HTTP verb of the incoming request, and calls the proper method accordingly.

The following incoming URL, included as part of an HTTP GET message, would map to the first Get() method as defined in the controller above:

Example URL With No ID Parameter
http://mydomain/values/

 

Similarly, this URL, also as part of an HTTP GET request, would map to the second Get(id) method:

Example URL Including ID Parameter
http://mydomain/values/5

 

We could, however, modify our controller thusly, and the routing would still work without modification:

Modifications to the ValuesController Class:
// GET api/values
public IEnumerable<string> GetValues()
{
returnnewstring[] { "value1", "value2" };
}
// GET api/values/5
publicstring GetValue(int id)
{
return"value";
}
// POST api/values
publicvoid PostValue([FromBody]Book value)
{
}
// PUT api/values/5
publicvoid PutValue(int id, [FromBody]stringvalue)
{
}
// DELETE api/values/5
publicvoid DeleteValue(int id)
{
}

 

Note that we retained the HTTP action verb as a prefix to all of our method names. Under these circumstances, the unmodified route parameter will still work just fine. When per convention, no {action} route parameter is specified, the Web API framework again appends the "Controller" suffix to the value provided for the {controller} parameter, and then scans the project for a suitably named class (in this case, one which derives from ApiController).

Action Method Selection

Once the proper controller class is selected, the framework examines the HTTP action verb of the request, and searches the class for method names with a matching prefix.

In order to determine the proper method, the framework then examines the additional URL parameters and attempts to match them with method arguments by name (case insensitive). The method with the most matching arguments will be selected.

An item to note here. Unlike in MVC, in Web API complex types are not allowed as part of the URL. Complex types must be placed in the body of the HTTP message. Also, there can be one, and only one such complex type in the message body.

In evaluating parameter matches against method arguments, any complex types and URL query strings are disregarded when searching for a match.

Web API and Action Naming

We can modify our default Web API route to include an {action} route parameter, in which case selection will occur similar to that in MVC. However, action methods defined on our controller still need to include the proper HTTP action verb prefix, and incoming URLs must use the full method name.

If we modify our default route thusly, adding an {action} parameter:

The Modified WebApiConfig Class and MapHttpRoute Method:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
namespace StandardWebApiTemplateProject
{
publicstaticclass WebApiConfig
    {
publicstaticvoid Register(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

 

To see how this works, consider the incoming URL:

http://mydomain/values/5

 

This will no longer work. If we wish to make a request of our modified ValuesController now, we would need to submit:

http://mydomain/values/GetValue/5

 

Review of the Basics for Web API Routing

  • The routing convention for Web API is to route URLs to a controller, and then to the action which matches the HTTP verb of the request message. Action methods on the controller must either match the HTTP action verb, or at least include the action verb as a prefix for the method name.
  • The default route template for a Web API Project is {controller}/{id} where the {id} parameter is optional.
  • Web API route templates may optionally include an {action} parameter. However, the action methods defined on the controller must be named with the proper HTTP action verb as a prefix in order for the routing to work.
  • In matching incoming HTTP messages to controllers, the Web API framework identifies the proper controller by appending the literal "Controller" to the value of the {controller} route parameter, then scans the project for a class matching that name.
  • Actions are selected from the controller by considering the non-complex route parameters, and matching them by name to the arguments of each method which matches the HTTP verb of the request. The method which matches the most parameters is selected.
  • Unlike MVC, URLs in Web API cannot contain complex types. Complex types must be placed in the HTTP message body. There may be one, and only one complex type in the body of an HTTP message.

That was Pretty Darn Basic. What Next?

The above represents a very simple overview of the Web API routing basics, focusing on the default configurations and conventions. In the next post we will examine more advanced routing considerations, and route customization.

Additional Resources

Customizing Routes in ASP.NET MVC

$
0
0

electric_wire_by_uminotorino-500-375Routing is one of the primary aspects of the MVC framework which makes MVC what it is. While that is possibly over-simplifying, the routing framework is where the MVC philosophy of "convention over configuration" is readily apparent.

Routing also represents one of the more potentially confounding aspects of MVC. Once we move beyond the basics, as our applications become more complex, it generally becomes necessary to customize our routes beyond the simple flexible default MVC route.

Image by ~uminotorino  Some rights reserved

Route customization is a complex topic. In this article we will look at some of the basic ways we can modify the conventional MVC routing mechanism to suit the needs of an application which requires more flexibility.

NOTE: most of the examples in this post are somewhat contrived, and arbitrary. My goal is to demonstrate some basic route customization options without getting distracted by the business case-specifics under which they might be required. I recognize that in the real world, the examples may or may not represent reasonable cases for customized routes.

In any sufficiently complex ASP.NET MVC project, it is likely you will need to add one or more custom routes which either supplement or replace the conventional MVC route. As we learned in Routing Basics in ASP.NET MVC, the default MVC route mapping is:

{controller}/{action}/{id}

 

This basic convention will handle a surprising number of potential routes. However, often there will be cases where more control is required, either to:

  • Route incoming requests to the proper application logic in complex scenarios.
  • Allow for well-structured URLs which reflect the structure of our site.
  • Create URL structures that are easy to type, and  "hackable" in the sense that a user, on examining the structure of a current URL, might reasonably make some navigation guesses based on that structure.

Route customization is achieved in a number of ways, usually by by combining modifications to the basic pattern of the route mapping, employing route default options to specify controllers and actions explicitly, and less frequently, using route constraints to limit the ways in which a route will "match" a particular URL segment or parameter.

URLs are Matched Against Routes in Order – The First Match Wins

Incoming URLs are compared to route patters in the order the patters appear in the route dictionary (that is what we added the route maps to in our RouteConfig.cs file). The first route which successfully matches a controller, action, and action parameters to either the parameters in the URL or the defaults defined as part of the route map will call into the specified controller and action. This is important, and requires us to think our routes through carefully so that the wrong handler is not called inadvertently.

The basic process of route matching was covered in Routing Basics in ASP.NET MVC.

Modifying the URL Pattern

As we learned previously, in an MVC application, routes define patterns by which incoming URLs are matched to specific controllers, and actions (methods) on those controllers. Route mappings recognize URLs as a pattern of segments separated (or delimited) by a slash (/) character. Each segment may contain various combinations of  literals (text values) and parameter placeholders. Parameter placeholders are identified in a route definition by braces.

MVC recognizes the special parameter placeholders {controller} and {action} and uses these to locate the appropriate controller and method to call in response to an incoming URL. In addition to these two special placeholders, we can add just about anything to a route as a parameter placeholder to suit our purpose, so long as we adhere to good URL design principles, and of course, reasonably expect the parameter to be useful.

We are familiar with the default MVC pattern shown above. We can see that the route is composed of three segments, with no literals, and parameter placeholders for the special parameters {controller} and {action}, as well as an additional parameter called {id}.

When creating route URL patterns, we can combine literals with parameter placeholders in any number of ways, so long as parameter placeholders are always separated by either a delimiter, or at least one literal character. The following are examples of valid route patterns (whether they are sensible or not is another issue):

Examples of Valid Route Patterns in ASP.NET MVC:
Route PatternURL Example
 mysite/{username}/{action} ~/mysite/jatten/login
 public/blog/{controller}-{action}/{postId} ~/public/blog/posts-show/123
 {country}-{lang}/{controller}/{action}/{id} ~/us-en/products/show/123
 products/buy/{productId}-{productName} ~/products/buy/2145-widgets

 

The following pattern is not valid, because the {controller} parameter placeholder and the {action} parameter placeholder are not separated by either a slash (/) or another literal character. In this case, the MVC framework has no way to know where one parameter ends, and the next begins (assume the controller is named "People" and the action is "Show'):

Route PatternURL Example
 mysite/{controller}{action}/{id} ~/mysite/peopleshow/5

 

Not all of the valid route examples above include either the {controller} parameter placeholder or the {action} parameter placeholder. The last example does not include either. Also, most include user-defined parameters, such as {username} or {country}. We'll take a look at both in the next two sections.

Modifying Route Default Options

In conjunction with modifying the route URL pattern, we can also take advantage of the route defaults to create routes which are more specific, and in some cases which map only to a specific controller and/or action.

The standard MVC project file contains the route mapping configuration in a file named RouteConfig.cs:

The standard MVC RouteConfig.cs File:
publicclass RouteConfig
{
publicstaticvoid RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { 
                controller = "Home", 
                action = "Index", 
                id = UrlParameter.Optional }
        );
    }
}

 

The route above establishes a default value for both the controller and the action, in case one more both are not provided as part of an incoming URL.

We can take this a step further, and add a new, more specific route which, when it matches a specific URL pattern, calls into one specific controller. In the following, we have added a new route mapping in our RouteConfig.cs file:

Adding a More Restrictive Route:
publicstaticvoid RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// ALL THE PROPERTIES:
// rentalProperties/
    routes.MapRoute(
        name: "Properties",
        url: "RentalProperties/{action}/{id}",
        defaults: new
        {
            controller = "RentalProperty",
            action = "All",
            id = UrlParameter.Optional
        }
    );
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { 
            controller = "Home", 
            action = "Index", 
            id = UrlParameter.Optional }
    );
}

 

First off, take note that we have added the new, more specific route before the default in the file (which determines the order it is added to the route dictionary). This is because routes are evaluated for a match to an incoming URL in order. The default MVC route a pretty general, and if route order is not carefully considered when adding additional routes to your project, the default route may inadvertently match a URL intended for a different handler.

Beyond this, what we see in the new route added above is that there is no {controller} parameter placeholder in the URL pattern of the "Properties" route. Since no controller can be passed to this route as a parameter, it will always map to the RentalPropertiesController , and to whichever Action is provided as a parameter. Since there is a default action defined, if the incoming URL matches the route but does not contain an Action parameter, the default All() method will be called.

A Contrived Example

So far, in this rather contrived example, we haven't accomplished anything we couldn't have done using the default MVC route pattern. However, suppose in our application we wanted to define the following URL patterns and associated URL examples for accessing views in a property management application:

Desired URL patterns for Simple Property Management Application:
BehaviorURL Example
 Show all the rental properties ~/rentalproperties/
 Show a specific rental property ~/rentalproperties/propertyname/
 Show a specific unit at a property ~/rentalproperties/propertyname/units/unitNo

 

We could define a RentalPropertyController class as follows:

Rental Property Controller Example:
publicclass RentalPropertiesController : Controller
{
private RentalPropertyTestData _data = new RentalPropertyTestData();
// List all the properties
public ActionResult All()
    {
        var allRentalProperties = _data.RentalProperties;
return View(allRentalProperties);
    }
// get a specific property, display details and list all units:
public ActionResult RentalProperty(string rentalPropertyName)
    {
        var rentalProperty = _data.RentalProperties.Find(a => a.Name == rentalPropertyName);
return View(rentalProperty);
    }
// get a specific unit at a specific property:
public ActionResult Unit(string rentalPropertyName, string unitNo)
    {
        var unit = _data.Units.Find(u => u.RentalProperty.Name == rentalPropertyName);
return View(unit);
    }
}

 

Given the above controller, and the desired URL patterns in the previous table, we can see that the default MVC route mapping would work for some, but not all of our URLs. We might be better served by adding the following controller-and-action-specific routes to our application, so that our RouteConfig.cs file looks like this:

Modified Route Config File for Property Management Example Application:
publicstaticvoid RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// RentalProperties/Boardwalk/Units/4A
    routes.MapRoute(
        name: "RentalPropertyUnit",
        url: "RentalProperties/{rentalPropertyName}/Units/{unitNo}",
        defaults: new
        {
            controller = "RentalProperties",
            action = "Unit",
        }
    );
// RentalProperties/Boardwalk
    routes.MapRoute(
        name: "RentalProperty",
        url: "RentalProperties/{rentalPropertyName}",
        defaults: new
        {
            controller = "RentalProperties",
            action = "RentalProperty",
        }
    );
// RentalProperties/
    routes.MapRoute(
        name: "RentalProperties",
        url: "RentalProperties",
        defaults: new
        {
            controller = "RentalProperties",
            action = "All",
            id = UrlParameter.Optional
        }
    );
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new
        {
            controller = "Home",
            action = "Index",
            id = UrlParameter.Optional
        }
    );
}

 

As we see now, all of the newly added routes explicitly specify both the controller and action, and allow for our progressively enhanced URL structure which includes the property name as part of the URL.

Of course, in the real world, this URL scheme would likely not work the way we desire, because (depending upon how our database is going to be used) the names of rental properties may not be unique, even within a given management company. If the company operated rental properties in several states or even counties within a state, it is entirely possible that there would be more than one property named (for example) "Maple Glen." It is a sad fact that despite all the wonderfully simple examples we can find while learning, the real world often fails to model in the way we want.

Adding Route Constraints

Route constraints can be used to further restrict the URLs that can be considered a match for a specific route. While we have so far examined whether routes match based upon URL structure and parameter names, Route Constraints allow us to add some additional specificity.

The MVC framework recognizes two types of constraints, either a string, or a class which implements the interface IRouteConstraint.

Regular Expressions in Route Constraints

When a string is provided as a constraint, the MVC framework interprets the string as a Regular Expression, which can be employed to limit the ways a URL might match a particular route. A common scenario in this regard would be to restrict the value of a  route parameter to numeric values.

Consider the following route mapping:

Example Route Mapping for Blog Application with no Constraint:
routes.MapRoute(
    name: "BlogPost",
    url: "blog/posts/{postId}",
    defaults: new
    {
        controller = "Posts",
        action = "GetPost",
    }, 
);

 

This route will call into the PostsController of a hypothetical blog application and retrieve a specific post, based on the unique postId. This route will properly match the following URL:

http://domain/blog/posts/123

Unfortunately, if will also match this:

http://domain/blog/posts/gimme

The basic Route Constraint allows us to test the value of the {postId} parameter to determine if it is a numeric value. We can re-write our route mapping thusly:

Example Route Mapping for Blog Application with Added Constraint:
routes.MapRoute(
    name: "BlogPost",
    url: "blog/posts/{postId}",
    defaults: new
    {
        controller = "Posts",
        action = "GetPost",
    }, 
new {postId = @"\d+" }
);

 

The tiny Regular Expression @"\d+ in the code above basically limits the matches for this route to URLs in which the postId parameter contains one or more digits. In other words, nothing but integers, please.

I'm not going to dig too deep into Regular Expressions here. Suffice it to say that Regular Expressions can be used to great effect in developing a complex routing scheme for your application.

A Note and More Resources for Regular Expressions

Regular Expressions are a powerful tool, and also a giant pain the the ass. As Jamie Zawinskie said, "Some people, when confronted with a problem, think 'I know, I'll use regular expressions.' Now they have two problems." However, Regular Expressions are sometimes the very best tool for the job – restricting route matches in an MVC application is one of those cases. I find three tools most helpful when writing more complex regular expressions. In order of importance:

Custom Route Constraints using IRouteConstraint

The other option for using route constraints is to create a class which implements the IRouteConstraint interface.

We'll take a quick look at a custom route constraint which can be used to make sure a particular controller is excluded as a match for our default MVC route.

Note: The following code was adapted from a code project article by Vijaya Anand (Code Project member After2050), originally posted at his personal blog Proud Parrot.

In creating a custom constraint, we first create a new class which implements IRouteConstraint. IRouteConstraint defines a single method, Match for which we need to provide the implementation. In order for the constraint to work, we will also need to create a constructor with which we set the argument(s) required for the method:

The Custom Constraint Class ExcludeController:
publicclass ExcludeController : IRouteConstraint
{
privatereadonlystring _controller;
public ExcludeController(string controller)
    {
        _controller = controller;
    }
publicbool Match(HttpContextBase httpContext,
        Route route, string parameterName,
        RouteValueDictionary values,
        RouteDirection routeDirection)
    {
// Does the _controller argument match the controller value in the route
// dictionary for the current request?
returnstring.Equals(values["controller"].ToString(),
            _controller, StringComparison.OrdinalIgnoreCase);
    }
}

 

Now, suppose we have a hypothetical controller ConfigurationController for which we have defined a special route which requires authentication. We don't want the MVC default route map to inadvertently allow access to the Configuration controller for unauthenticated users. We can make sure of this by modifying our default MVC route like so:

Adding a Custom Constraint to the Default MVC Route:
routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new
    {
        controller = "Home",
        action = "Index",
        id = UrlParameter.Optional
    },
    constraints: new { controller = new ExcludeController("Configuration")}
);

 

Now, if a URL which has not been matched by any previous route mapping is evaluated for a match with our default route, the MVC framework will call the Match method of our custom constraint ExcludeController. In the case above, if the URL contains the {controller} parameter with a value of "Configuration" the Match method will return a value of false, causing the route to reject the URL as a match.

Stephen Walther presents an excellent tutorial on Custom Route Constraints, and specifically, creating a authentication constraint at his blog in ASP.NET MVC Tip #30 – Create Custom Route Constraints.

8/23/2013 – UPDATE: I forgot to mention that Attribute Routing is an alternative routing model which allows you to specify routes directly on the controller action handler for a specific URL pattern. You can use Attribute Routing now as a Nuget package, but it is actually going to be an integrated option shipped with the upcoming release of ASP.NET 5 and Web Api 2. There is some debate as to whether Attribute Routing is a good thing, as evidenced by K. Scott Allen's recent post on OdeToCode. In this article I keep the focus on customization of the standard routing mechanism in ASP.NET.

Additional Resources

ASP.NET MVC: Configuring ASP.NET MVC 4 Membership with a SQL CE Database

$
0
0

technology-of-biug-data-500Recently I needed to set up an ASP.NET MVC project to work with a SQL CE database. I also needed to use SQL CE as the backing store for the ASP.NET Membership feature. This was a simple, quick-and-dirty project for work, and the database requirements were very, very simple. I wanted to use SQL CE for all the data, and also use SQL CE for the membership function so that everything was nicely buttoned up within my site.

Turns out it was not 100% intuitive to make this work, but here is how I did it.

Image by Infocux Technologies | Some Rights Reserved

Why Use SQL CE?

SQL CE is a great choice for applications which are not data-intensive, and/or for which deployment with minimal or no external dependencies is important. SQL CE is an embedded database, and includes all required runtime dependencies within the application. In other words, no need to install and configure SQL Server or SQL Express.

SQL CE was an ideal choice for my little work project, for which storage requirements were minimal, but for which I needed some level of membership/authorization. Once I had the configuration described below in place, development and (especially) deployment were a breeze, with no SQL Server configuration hassle.

The default configuration is for SQL Server Express

Out of the box, an ASP.NET MVC Project is configured to use SQL Server Express as the membership provider. If you do File –> New Project and choose an MVC 4 project type, your  Web.config file will contain a <connectionStrings> element that looks something like this:

<connectionStrings>
<addname="DefaultConnection"
connectionString="Data Source=(LocalDb)\v11.0;Initial 
     Catalog=aspnet-ConfigureAspNetforSQLCeMembershipDb-20131006163451;Integrated 
     Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-YourProject-20131006163451.mdf"
providerName="System.Data.SqlClient"/>
</connectionStrings>

 

If you run the out-of-the-box site and "register", a YourProject.mdf file and an accompanying .log file will be created in the App_Data folder in the VS solution.

If we want to use SQL CE instead of SQL Server Express, we need to change some things up.

Get the Microsoft.AspNet.Providers.SqlCe Package using Nuget

First off, we need to install the Microsoft Universal Provider for SQL CE. There is a handy Nuget package for doing just this. We can either open the Nuget Package Manager Console and do:

Install Microsoft.AspNet.Providers.SqlCE from the Package Manager Console:
PM> Install-Package Microsoft.AspNet.Providers.SqlCE

 

Or, we can go to the Solution Explorer, right-click on the solution, and select "Manage Nuget Packages for Solution. Then, select "Online" from the left-hand menu, and type Microsoft.AspNet.Providers.SqlCe in the search box:

Install Microsoft.AspNet.Providers.SqlCE from the Package Manager Console:

get-sqlCE-provider-with-nuget-package-manager

One you have done this, there will now be an extra entry in the <connectionStrings> element of your Web.config file:

Extra DefaultConnection in Web.config File:
<connectionStrings>
<addname="DefaultConnection"
connectionString="Data Source=(LocalDb)\v11.0;Initial 
     Catalog=aspnet-YourProject-20131006170652;Integrated 
     Security=SSPI;AttachDBFilename=|DataDirectory|\aspnet-YourProject-20131006170652.mdf"
providerName="System.Data.SqlClient"/>
<addname="DefaultConnection"
connectionString="Data Source=|DataDirectory|\aspnet.sdf"
providerName="System.Data.SqlServerCe.4.0"/>
</connectionStrings>

 

The new <add name= "DefaultConnection" . . . > node was added for us when we installed the SqlCe provider package. This will now conflict with the previous entry, so delete the original (where providerName = "System.Data.SqlClient" ).

That was easy. But we're not done yet.

Configure the Profile, Membership, and Role Providers in Web.config

In order for SQL CE to work as the back-end store for the membership features of our MVC site, we need to do some tuning on the Profile providers section of our Web.config file as well. If you scroll down from the connectionStrings section, you will find the Profile, Membership, and Role nodes, which looks something like this:

Default Profile, Membership, and Role Provider Configuration in Web.config:
<profiledefaultProvider="DefaultProfileProvider">
<providers>
<addname="DefaultProfileProvider"type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"connectionStringName="DefaultConnection"applicationName="/"/>
</providers>
</profile>
<membershipdefaultProvider="DefaultMembershipProvider">
<providers>
<addname="DefaultMembershipProvider"type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"connectionStringName="DefaultConnection"enablePasswordRetrieval="false"enablePasswordReset="true"requiresQuestionAndAnswer="false"requiresUniqueEmail="false"maxInvalidPasswordAttempts="5"minRequiredPasswordLength="6"minRequiredNonalphanumericCharacters="0"passwordAttemptWindow="10"applicationName="/"/>
</providers>
</membership>
<roleManagerdefaultProvider="DefaultRoleProvider">
<providers>
<addname="DefaultRoleProvider"type="System.Web.Providers.DefaultRoleProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"connectionStringName="DefaultConnection"applicationName="/"/>
</providers>
</roleManager>

 

In order to use SQL CE for our Membership provider, we need to replace the above with the following:

Modified Profile, Membership, and Role Provider Configuration in Web.config:
<profiledefaultProvider="DefaultProfileProvider">
<providers>
<clear/>
<addname="SimpleRoleProvider"type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/>
</providers>
</profile>
<membershipdefaultProvider="SimpleMembershipProvider">
<providers>
<clear/>
<addname="SimpleMembershipProvider"type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData"/>
</providers>
</membership>
<roleManagerenabled="true"defaultProvider="SimpleRoleProvider">
<providers>
<clear/>
<addname="SimpleRoleProvider"type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/>
</providers>
</roleManager>

 

Once this is done, go to the App_Data folder, and if you originally ran the project and created a SQL Express Database (.mdf and .ldf files), delete those.

Run the Project, and a SQL CE Membership Database will be Created

In theory, that should be it. Once you run the project again, an .sdf file will be created in your App_Data folder and you should be off an running.

In reality, I had to do some fussing around. If you are using IIS Express for running your application in VS (this is the default), you may need to go to the system tray and Exit IIS Express, or at least stop the site, before you run again.

If you have any other issues, please feel free to let me know in the comments section. I really want to provide complete information here, even for simple things like this.

Other Resources

If you are newer to ASP.NET MVC, you might find some of the following helpful:

Reverse-Engineering an Existing Database in your ASP.NET MVC Application Using Entity Framework Model/Database-First

$
0
0

gears-320Entity Framework's "Code-First" development model has become a highly popular approach to data modeling in ASP.NET MVC development in the last few years. The Code First approach saves the average developer the perceived pain of working with SQL, and also minimizes the amount of repetitive, "boiler plate" code we need to write as part of a more traditional data access layer.

However, quite frequently we will need to develop against an existing database, the schema of which may or may not be open for modification.

Image by Ian Britton | Some Rights Reserved

While it is possible to reverse-engineer an existing database and then switch to a code-first approach once the models have been created, for various reasons this approach may be impossible or impractical. In certain cases we must start with a pre-defined database schema and/or allow database changes to drive our model instead of the other way around. In this post we will take a quick look at how to reverse engineer an existing database to create your conceptual data model.

Within the context of an ASP.NET MVC application, we may find ourselves looking at the Database or Model First approach in a number of different situations, including:

  • Creating a new front end against an existing database
  • An organizational or DBA philosophy driven by the modeling the database first
  • Personal developer preference – some of us simply prefer to model the data first.

For Our Examples: The Database . . . First . . .

Under the premise that we must (for whatever reason) build out our application around an existing database, we will create an extremely complex SQL Server database and pretend it is what we have been given by the powers that be:

Example Database:

simple-example-database-diagram

As you can see, we have a complicated database with two related tables. Now, let's look at an example ASP.NET MVC project, into which we will reverse engineer this database to create our model classes and database context.

Create an ADO.NET Entity Data Model

In a standard ASP.NET project, we should find a pre-defined Models folder, in which there will (usually) already be an Account Model related to ASP.NET Forms Membership. Right-click on the Models folder in the VS Solution Explorer and select Add => ADO.NET Entity Data Model:

Adding an ADO.NET Entity Data Model:

add-ado-net-entity-data-model

After you do this, you will be prompted to provide a name for the data model you are about to create. The convention is something along the lines of "<ProjectName>Entities." In this case, I chose "ExampleEntities" and then proceed to the next step, where we are asked to choose what the model should contain. Obviously, we want to select "Generate from database" here, and then select Next in the Wizard.

In the next window, we are asked to choose a connection:

Choose Your Data Connection (or create a New One):

choose-connection1

Obviously, our database is not called "ChinookDb.sdf" as seen in the picture above, and  in fact we probably don't already have our database available as an existing connection, so we need to click the "New Connection" button and create one. Once that's done, you will notice the wizard automatically determined an appropriate name (in my case, "ExampleDbEntities" for our connection string in Web.config. You can change this if you like, but I recommend using the default for consistency. Hit "Next."

In the next window, we are asked to select the database objects we wish to include in our model. For our simple database (and in most cases) we just want the tables. In my case, there is an extra table "sysDiagrams" that is NOT part of our domain model. In your own project, select the tables which make sense as part of your model domain. The other default settings in this window are usually appropriate as well:

Select the Database Objects:

select-database-objects

Now, hit "Finish."

VS will take a few seconds to reverse engineer your database, and when finished, will present you with a diagram of the conceptual model of your database. As you can see, VS made fast work of our extremely complex data model:

The EDMX Diagram for ExampleEntities:

edmx-model-diagram

We can find our generated model classes in Solution Explorer under Models => ExampleEntities => ExampleEntities.tt:

The New Model Classes in VS Solution Explorer:

 

where-are-my-model-classes

 

Add Functionality to Generated Classes with Partial Classes

Now that we have our model classes, it may often be the case we need to add functionality to them to suit the requirements of our program. Under the model-first or Database First approach, there are some things to be aware of here. These classes are generated, and can be automagically updated to reflect changes in the underlying database. If and when you do this, they will be re-written, and any code you may have added will be gone. Therefore, when you need to add additional properties or methods to one of these classes, use partial classes and add them in the Models folder.

For a contrived example, let's say we absolutely needed a CountOfAlbums property defined on our Artist class. We COULD open up our Artist class and simply add a new method like so:

Modifying the Artist Class in the Generated File:
//------------------------------------------------------------------------------
// <auto-generated>
//    This code was generated from a template.
//
//    Manual changes to this file may cause unexpected behavior in your application.
//    Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace AzureModelFirstExample.Models
{
using System;
using System.Collections.Generic;
public partial class Artist
    {
public Artist()
        {
this.Albums = new HashSet<Album>();
        }
publicint Id { get; set; }
publicstring Artist1 { get; set; }
publicvirtual ICollection<Album> Albums { get; set; }
// THIS IS OUR NEW METHOD:
publicint CountAlbums
        {
get
            {
returnthis.Albums.Count;
            }
        }
    }
}

 

However, if later we were to have EF update our model to reflect changes in the underlying database schema (We'll look at this in a moment), this addition would be over-written. A better approach would be to add a new class file named ArtistPartial to our Models folder. Once we have done this by right-clicking in Solution Explorer => Add => Class . . . , we get a code file which looks like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace AzureModelFirstExample.Models
{
publicclass ArtistPartial
    {
    }
}

 

Now, we will leave the actual code file name as ArtistPartial.cs, but we can replace the class declaration using the partial keyword and the class name Artist to match our generated model class. Then we can put our custom property in this file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace AzureModelFirstExample.Models
{
public partial class Artist
    {
// THIS IS OUR NEW METHOD:
publicint CountAlbums
        {
get
            {
returnthis.Albums.Count;
            }
        }
    }
}

 

Now, we can simply add the partial keyword to the class declaration for our EF-generated Artist model class (which we may need to do again if we regenerate the code as well, but it is easier to deal with than re-writing one or more potentially complex properties or methods . . .):

//------------------------------------------------------------------------------
// <auto-generated>
//    This code was generated from a template.
//
//    Manual changes to this file may cause unexpected behavior in your application.
//    Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace AzureModelFirstExample.Models
{
using System;
using System.Collections.Generic;
public partial class Artist
    {
public Artist()
        {
this.Albums = new HashSet<Album>();
        }
publicint Id { get; set; }
publicstring Artist1 { get; set; }
publicvirtual ICollection<Album> Albums { get; set; }
    }
}

 

Adding MetaData Annotations to Generated Classes using Partial Classes

ASP.NET MVC provides some powerful and time-saving validation and other features which rely on Data Annotations facilitated in System.ComponentModel.DataAnnotations. Important attribute decorations for our model classes such as [Required] and [DisplayName] allow us to let the MVC framework and JQuery provide basic data entry validations for our site right out of the box.

As generated, our simple model classes don't know anything about whether (for example) NULL values are allowed in the database. If we want to take advantage of MVC's out-of-the-box validation and other helpers with our generated classes, we need to find a way to implement the data annotations and other metadata attributes. As before, if we implement these annotations directly in the generated Artist (or any other EF-generated) code file, they would again be over-written any time the model is updated. However, we can do this in our partial class, and thus preserve our validations through successive model updates throughout the development process. As an example, we know that:

  • The Artist1 property of our Artist class is required, as in our database null values are not allowed (and we ALL KNOW that an empty string is NOT THE SAME AS NULL . . . RIGHT??).
  • Artist1 is a terrible name for what should be the "Name" property of the artist. However, in our database. we would like to be able to give this a [Display] name attribute so that in our views, MVC will use a more appropriate label (such as "Name" or simply "Artist").

We can achieve both objectives by adding the following to our ArtistPartial code file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
// This must be added to our using statements:
using System.ComponentModel.DataAnnotations;
namespace AzureModelFirstExample.Models
{
// We have added a class-level MetaData attribute here:
    [MetadataType(typeof(ArtistMetadata))]
public partial class Artist
    {
// THIS IS OUR NEW METHOD:
publicint CountAlbums
        {
get
            {
returnthis.Albums.Count;
            }
        }
    }
// We have defined and ArtistMetaData class which defines the 
// meta-data attributes for various properties on the artist class:
publicclass ArtistMetadata
    {
        [Required]
        [Display(Name = "Artist Id")]
publicstring Id { get; set; }
        [Required]
        [Display(Name = "Name")]
publicstring Artist1 { get; set; }
    }
}

 

Make sure to add the using System.ComponentModel.DataAnnotations to your using statements for the above to work properly.

As you can see, we added a new ArtistMetaData class to our ArtistPartial.cs code file, and defined the data annotations we wanted for each of the properties. To me, the one of greatest import is the [Required] attribute, which allows the MVC framework to perform JQuery validations right from the start.

When we work under a code-first development model, we would define these and other attributes on our classes, and EF would use this metadata while creating our database. In Model/Database first development, we need to decorate our classes manually. Doing this using partial classes allows us to preserve the annotations through successive automated updates to the model.

Updating the Model to Reflect Changes in the Underlying Database

Imagine the harsh, cruel DBA (aren't they all? – kidding, of course) at our company informs us that he has added a new table, RecordLabels to the database, and created a foreign key on the Albums table to reflect the label for each particular album.

No big deal, Entity Framework has us covered, right?

We can simply open our EDMX diagram, right-click anywhere in the viewing area, and select "Update model from database . . ." :

Updating the Model to Reflect Changes in the Database Schema:

update-model-from-database

 

Once we select this option, we see a familiar window. However, there are some differences. Once again, we will select the new tables to add. Again in my case sysDiagrams appears as an option, as well as the new RecordLabels table our cruel DBA added. We only want to select the RecordLabels table to add:

Select the RecordLabels Table to Add to the EF Model:

update-add-new-table-to-model

This time, however, we also see a tab named "Refresh." Here we find any database objects which have been changed since the last model update. In our case, we  have added a foreign key to the Albums table. In my specific case, the Artist table also appears, because I modified a few things in the course of preparing these examples.

Database Objects Modified Since the Last Model Update:

update-refresh-existing

Once we click the "Finish" button, VS will again take a moment to regenerate our model classes. Once we Rebuild the project, we find that we now have the new model class RecordLabel in our ExampleEntities.tt file. Further, if we open our Album class we see that EF has now added a LabelId field, and a RecordLabel class as a property.

Last, since we carefully defined any additional methods required for our classes, along with relevant meta-data annotations, in respective partial class files for each of our model classes, our custom code for each class is untouched. We may need to modify it to reflect some of the changes (for example, we have added a LabelId field to the Album class, which likely should be non-nullable in the database and hence [Required] in our model.

We may need to go back through and add the partial keyword to some of our classes, although in some cases EF can figure out that it needs this as well.

Also, we should probably create a similar partial class file for our new RecordLabel class, and add what annotations and additional code make sense. 

Why Model or Database First?

The Entity Framework Code-First approach seems to be the new shiny thing for use in developing database applications. From a strictly developer standpoint, the Code First approach may be attractive in that:

  • We can (to an extent) allow our database to be modeled upon our object domain, and we get to work and think in the familiar Object-Oriented realm we understand the best;
  • Changes to the underlying data base structure can be propagated automatically as our code evolves
  • We don't have to deal directly with SQL (for some, this is a strangely big deal).

However, there are plenty of times when the Code First approach is either not an option (remember the cruel DBA with the existing database we are required to use?), or when as a matter of preference we choose to work with the data model first, and build our code on top of that. In these situations, it is important to recognize the small but significant differences between the two approaches, and build out our application accordingly.

Additional Resources and Items of Interest

ASP.NET MVC–Azure Websites and EF Model First Connection Strings when Deploying from Github

$
0
0

guitar-strings-320One of the coolest things about Windows Azure Websites is the integration with source control, and the automated deployment hooks Azure offers. However, there are a number of small gotcha's related to deployment, chief among these is the protection of confidential configuration items such as database connection string.

In this post, we are going to look specifically at the vexing situation I ran into deploying from source control with an application using Entity Framework's "Model-First" strategy (everything here is pretty much applicable to "Database-First" as well, since in terms of the conceptual modeling of the database within the application, they are the same).

Image by Hammad Ali | Some Rights Reserved

NOTE: If you are an MSDN Subscriber, you can access free Azure benefits. If you are not an MSDN subscriber, you can still have up to 10 Windows Azure Websites for free. Additionally, anyone can take advantage of an introductory free 30 day trial to check out the rest of the Azure platform.

If you wish to take advantage of Windows Azure Websites Deployment from Source Control feature, and your application uses Entity Framework's Model-or-Database-First approach, there are some non-intuitive set-up concerns that need to be addressed before your deployment will work properly.


EF Model-First Connection String Meta-Data

When you use Entity Framework to either build a database based upon the model you create in the VS Entity Designer, or use EF to reverse-engineer a conceptual model from an existing database, EF creates a bunch of meta-data mappings used to manage the interaction of your application with the database.

Unlike the "Code-First" approach, in which much of the meta-data exists as annotations on your model classes which EF then uses to create and map the relations of your classes to the database, the Model-First paradigm leaves you with straight POCO ("Plain Old CLR Objects" or, alternately, "Plain Old Class Objects") classes.

In order for EF (and thus, your application) to work properly in a Model-First approach, it needs to know where all this meta-data lives, and this is evident in the connection string created by EF in your Web.config file:

EF Model First Connection Strings in Web.config:
<connectionStrings>
<addname="DefaultConnection"
connectionString="Data Source=(LocalDb)\v11.0;
    Initial Catalog=aspnet-AzureModelFirstExample-20131020060646;
    Integrated Security=SSPI;
    AttachDBFilename=|DataDirectory|\aspnet-AzureModelFirstExample-20131020060646.mdf"
providerName="System.Data.SqlClient"/>
<addname="ExampleDbEntities"
connectionString="metadata=res://*/Models.ExampleEntities.csdl
     |res://*/Models.ExampleEntities.ssdl|res://*/Models.ExampleEntities.msl;
     provider=System.Data.SqlClient;
     provider connection string=&quot;data source=XIVMAIN\XIVSQL;
     initial catalog=ExampleDb;integrated security=True;
     MultipleActiveResultSets=True;App=EntityFramework&quot;"
providerName="System.Data.EntityClient"/>
</connectionStrings>

 

In the above, the first XML node in <connectionStrings> , named "DefaultConnection" is the connection string for the standard Forms Membership feature included out of the box with the default MVC project template.

The second node, for the connection string named "ExampleDbEntities" is what you get when you create an Entity Framework Model First implementation of the actual domain database your application will consume. All that metadata=res: stuff essentially tells EF where to find the meta data it needs to properly relate your generated model classes to database tables, and to keep track of the relationships between them.

In the example above, the membership database exists in the App_Data folder of the project. The ExampleEntities database is hosted on my local development server. As a result, both connections are able to use Windows Integrated Security, and no confidential connection information is persisted in the Web.config file. 

When deploying a website to Windows Azure Websites, there are any number of variations with respect to how the two-database application above might be implemented. You might create a separate membership database on your Azure account, as well as an application database, similar to the above.

More likely, you might decide to combine the two such that all of your application data is served by the same Azure SQL Database instance. For this article, we are going to look at the free Websites tier, which allows one free Azure SQL Database (<= 20 Mb) for use by all of your free websites. We will also assume that you must (for whatever reason) develop your application against an existing database schema, such that the Model/Database First approach is indicated.

Windows Azure Databases, Website Deployment, and Connection Strings

In the configuration section of the Azure Website Management Portal, there is a section specifically for setting up connection strings. Azure connection strings are key/value pairs where the NAME field (the Key) is the name of the connection string as defined in our Web.config file, and the VALUE represents the actual connection string to be used to connect to our Azure SQL Database instance.

Connection String Management in the Windows Azure Websites Portal

azure-configuration-connection-strings1

The above is from the Configuration portal for an example I set up. I have an existing Azure SQL Database set up, and when I created the example site, the DefaultConnection string called out in red was associated with that database.

When we deploy our site from source control, Azure maps the connection string names defined in our Web.config file to the correspondingly-named connection strings defined here. This preserves the privacy of our confidential Azure database connection information, since it does not need to be committed to our source control repo.

Recall from the previous section that our Membership database on our local machine also uses a connection string named "DefaultConnection." Since for our example we are presuming that in deployment the Membership tables actually reside in the same database as the rest of our application data, the connection string above is appropriate. Of course, we need to make sure to migrate our Membership tables into this database (or assume they are already there).

In any case, this connection string should point to wherever our membership tables live. As it sits currently, the DefaultConnection is a standard Azure SQL Database connection. If we click on the "Show Connection Strings" link, we see that with a few small exceptions, the Azure connection string closely resembles a standard SQL Server connection string:

Default Azure Connection String for a Linked Azure SQL Database
Data Source=tcp:yourAzureServerName.database.windows.net,1433;
    Initial Catalog=ModelFirstExampleDb;
    User ID=yourDbUserName@yourAzureServerName;
    Password=YourPassword

 

This should work properly with the membership functions of our application out of the box, so long as the Membership data resides in the target database to which this connection string points.

Now we come to the issue of our application database. Since our Entity Framework models expect a Model-First implementation, they will be looking for that funky, meta-data-laden second connection string we saw in our Web.config file.

The Model-First Connection String Pattern for Windows Azure

In order for Windows Azure to work with our EF Model-First Deployment, we need to set up the second connection string very specifically. First off, we need to name it to match the name defined for our application data connection string in Web.config. Second, we need to use the following pattern, and carefully substitute all of the <angleBracketed> values with our proper values (the angle brackets are not part of the connection string – remove those when putting your own values in):

The Model-First Connection String for Windows Azure SQL Database:
metadata=res://*/Models.<EFModel>.csdl|res://*/Models.<EFModel>.ssdl|res://*/Models.<EFModel>.msl;
provider=System.Data.SqlClient;
provider connection string=
"data source=tcp:<AzureServer>.database.windows.net;
initial catalog=<AzureDatabaseName>;
Persist Security Info=True;
User ID=<UserLogIn>@<AzureServer>;
Password=<UserPassword>"

 

Obviously, this should actually be a single line, but I have condensed it here as much as was reasonable for readability. You can find the unmodified template at my Github repo.

  • The value for <EFModel> is the name of the Entity Framework Model in your project
  • The value for <AzureDatabaseName> is obviously, the name of your deployed Azure SQL database.
  • When you initially set up your SQL Database, you will have created the values for <UserLogIn> and <UserPassword> . Replace those bracketed items with the values you created with the database.
  • To find the value to use for <AzureServer> , navigate to your Azure SQL Database management area in the Azure Portal SQL Databases => Database Name => Dashboard. Replace <AzureServer> with the value from your own configuration called out (but redacted) in red below:
Find the Value for <AzureServer> in SQL Database Dashboard:

locate-azure-server-name

Filling in the Example Azure Connection String Settings

So, that all seems like a lot to have read just to get to this, but here goes . . . Your DefaultConnection is already defined, and points directly at your Azure SQL Database. Assuming your ASP.NET Membership tables are all defined in your one application database, all we need to do is paste in our EF Model-First connection string, once we have replaced the placeholders with the values for our own SQL Database, and configure the rest of the connection string (again, it is important that the NAME value here match the name defined for this connection in Web.config).

In the case of our initial example from the beginning of this post:

  • The NAME is ExampleDbEntities
  • The Connection String for the example at the beginning of the post is (Remember this actually needs to be all in a single line, not formatted with line returns like this)::
metadata=res://*/Models.ExampleEntities.csdl|res://*/Models.ExampleEntities.ssdl|res://*/Models.ExampleEntities.msl;
provider=System.Data.SqlClient;
provider connection string=
"data source=tcp:MyAzureServer.database.windows.net;
initial catalog=ModelFirstExampleDb;
Persist Security Info=True;
User ID=MyUserLogin@MyAzureServer;
Password=MySuperSecretPassword"

 

Lastly, there is a drop-down selector for the provider type. The default is SQL Databases, but for Entity Framework, we want to select the "Custom" option.

If we were configuring the example from the beginning of this post, the configuration settings on the Azure portal would look something like this:

configure-azure-settings-values

Once you have entered all these values, Click the Save icon at the bottom of the window. \

Troubleshooting

There are a lot of places for this to go just slightly wrong, thus borking your connection. For example, if you are developing against a local development database, and it is named slightly different than your Azure SQL Database, your will get an exception if this is not correctly addressed in the Azure Connection string. Also, there are simply a lot of places where a typographical error might creep in when replacing the values in the connection string template with your actual values.

Another area to keep an eye on is that your production database on Azure is identical to your development database. While we are working under the premise that the database is driving our development, it is possible that changes may be introduced to one and not the other.

Unfortunately, when we work in the Model-First approach, we don't get the easy database creation during deployment. Therefore, it is important to keep the database schema's in sync.

Windows Azure offers a number of helpful features in this regard. I will take a closer look at the overall building out data-backed websites on Windows Azure in an upcoming post.

Additional Resources and Items of Interest


Configuring Db Connection and Code-First Migration for Identity Accounts in ASP.NET MVC 5 and Visual Studio 2013

$
0
0

Tree-320The recent release of Visual Studio 2013 and ASP.NET MVC 5 brought significant changes to the Accounts management system (formerly ASP.NET Forms Membership). Also, there have been some minor changes in how we manage Entity Framework Code-First Migrations.

In this post we will review setting up the basic Identity accounts, pointing them at an external SQL Server (or whatever other database you choose) instance instead of the default (local) SQL Server CE or SQL Express database in App_Data and configuring Entity Framework Migrations so seed the database with initial data.

Image by Wonderlane | Some Rights Reserved

The Basic Components of the ASP.NET Identity System

Out of the box, when you create an ASP.NET MVC 5 project using the default template in Visual Studio 2013, your get a basic, ready-to-run website with the elementary Identity and Account management classes already in place. In the current configuration, the default action is that, when you run the application fro the first time and register as a user, the database will be created as a SQL Server CE (.sfd) or SQL Express (.mdf) file in the App_Data folder in your project.

The Identity Account classes in the Default MVC 5 Project Solution Explorer:

solution-explorer-identity-classes

 

In the above, the IdentityModel.cs file is where the essential Identity system components are defined. Opening that file in the code editor, we see two classes defined:

Code in the IdentityModel.cs Code File:
using Microsoft.AspNet.Identity.EntityFramework;
namespace DbMigrationExample.Models
{
publicclass ApplicationUser : IdentityUser
    {
    }
publicclass ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }
    }
}

 

The ApplicationUser class, which inherits from a framework class IdentityUser. This is the basic identity unit for managing individual accounts in the ASP.NET MVC 5 Account system. This class is empty as defined in the default project code, and so brings with it only those properties exposed by the base class IdentityUser. However, we can extend the ApplicationUser class by adding our own properties, which will then be reflected in the generated database. More on this in a moment.

We also find here the class ApplicationDbContext. This is the Entity Framework context used to manage interaction between our application and the database where our Account data is persisted (which may, or may not be the same database that will be used by the rest of our application). Important to note that this class inherits not from DBContext (as is the usual case with EF), but instead from IdentityDbContext. In other words, ApplicationDbContext inherits from a pre-defined DB context defined as part of Microsoft.AspNet.Identity.EntityFramework which contains the "Code-First" base classes for the Identity system.

Between these two classes, the MVC framework has provided the basics for generating and consuming the complete Identity Account database. However, the basic classes are extensible, so we can tailor things to suit our needs.

Lastly, note the AccountViewModels.cs file. Here are defined the ViewModels which are actually used by the Views in our application, such that only that information needed to populate a view and perform whatever actions need to be performed is exposed to the public-facing web. View Models are not only an effective design component from an architectural standpoint, but also prevent exposing more data than necessary.

Configuring the Database Connection

As mentioned previously, the default MVC project will create a SQL CE or SQL Express in the project's App_Data folder unless we tell it to do otherwise. All we really need to do is change the connection string used by the ApplicationDbContext, and point it at our target database.

The ApplicationDbContext class passes a connection string name to the base class in the constructor, namely "DefaultConnection." If we open the Web.config file, we find that under the <connectionStrings> element there is a single node, in which the "DefaultConnection" string is added to the connection strings collection:

The Default Connection String in Web.config:
<connectionStrings>
<addname="DefaultConnection"
connectionString="Data Source=(LocalDb)\v110;
    AttachDbFilename=|DataDirectory|
    \aspnet-DbMigrationExample-20131027114355.mdf;
    Initial Catalog=aspnet-DbMigrationExample-20131027114355;
    Integrated Security=True"
providerName="System.Data.SqlClient"/>
</connectionStrings>

 

The easiest way to change our target database is to simply change the connection string details for "DefaultConnection" in Web.config. In this case, we will swap out the SQL CE connection for a SQL Server development database I have running on my development machine (obviously, you can point this at any database you wish, or simply leave it set to use the local SQL CE database):

Pointing "DefaultConnection" at a Local SQL Server Instance:
<connectionStrings>
<addname="DefaultConnection"
connectionString="Data Source=XIVMAIN\XIVSQL;
    Initial Catalog=DbMigrationExample;Integrated Security=True"
providerName="System.Data.SqlClient"/>
</connectionStrings>

 

Now, if I run the application, I am greeted with the default home screen offered by the VS 2013 MVC project template:

The Home Page of the Default MVC Project Template:

default-home-page-aspnet-mvc-5-before-register

From here, I click on the "register" link upper right:

The Registration Page of the Default MVC Project Template:

default-home-page-aspnet-mvc-5-register

When I complete registration and hit the "Register" button, I am redirected back to the home page. However, we can see now, in the upper right corner, that I am, in fact, signed in as a registered user:

default-home-page-aspnet-mvc-5-after-register

None of this is surprising. The reason we just went through that little exercise was to see the resulting database created once we registered. If I open SQL Server Management Studio (SSMS), I should find a new Database named DbMigrationExample:

The New SQL Server Database Viewed in SSMS:

SSMS-new-database

Note the tables created here. Despite the fact that only ApplicationUser is defined in our application, all of the above are created as a result of the IdentityDbContext class from which ApplicationDbContext inherits.

The default project configuration only actually makes use of the data from dbo.AspNetUsers. However, you can see that a full range of identity management tables have been created, including those for role management, and external authorization (using Google/Facebook/Twitter accounts).

Configuring Entity Framework Migrations and Seeding the Database

As we develop, we may make changes to our classes which need to be reflected in the database. Also, quite often we would like to redeploy our development database either in a fresh state, or possibly with some initial data (or Seed data) present. As with previous version of Entity Framework and MVC, we can use EF Code First Migrations to do this.

Before proceeding, I am going to delete the SQL Server database created when I registered on the site and start fresh. If you did something similar, do the same.

In order to start using Migrations with your project, go to the tools menu in Visual Studio, and select Library Package Manager => Package Manager Console. When the console opens at the bottom of the screen, we simply type:

PM> Enable-Migrations –EnableAutomaticMigrations

 

Once we hit enter, the console will be busy for a moment as the Migrations folder is configured in our project. When the task is complete, our console window should look like this:

Console After Enable-Migrations Command:

console-enable-migrations

 

Seeding the Database with an Initial User Records

For various reasons we may want to deploy our database with some initial records in place . We may need tables for certain static values pre-populated, or we might simply want some test data to work with in development. In our current case, we will deploy the database with a couple of User records pre-populated for testing purposes.

Once we have run the Enable-Migrations command as above, there should be a Migrations folder at the root of our project. If we open Configuration.cs file in that folder, we see this:

Default Code in Migrations Configuration Class:
namespace DbMigrationExample.Migrations
{
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
internalsealedclass Configuration 
        : DbMigrationsConfiguration<DbMigrationExample.Models.ApplicationDbContext>
    {
public Configuration()
        {
            AutomaticMigrationsEnabled = true;
        }
protectedoverridevoid Seed(
            DbMigrationExample.Models.ApplicationDbContext context)
        {
//  This method will be called after migrating to the latest version.
//  You can use the DbSet<T>.AddOrUpdate() helper extension method 
//  to avoid creating duplicate seed data. E.g.
//
//    context.People.AddOrUpdate(
//      p => p.FullName,
//      new Person { FullName = "Andrew Peters" },
//      new Person { FullName = "Brice Lambson" },
//      new Person { FullName = "Rowan Miller" }
//    );
//
        }
    }
}

 

 

We want to modify the Configuration class as follows so that our test user records are created whenever the Database is created. Note that we have added some using statements to the top of the file as well to being in the Microsoft.AspNet.Identity and Microsoft.AspNet.Identity.EntityFramework, namespaces, as well as the Models namespace from our own project:

Modified Code in Migrations Configuration Class:
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using DbMigrationExample.Models;
namespace DbMigrationExample.Migrations
{
internalsealedclass Configuration 
        : DbMigrationsConfiguration<DbMigrationExample.Models.ApplicationDbContext>
    {
public Configuration()
        {
            AutomaticMigrationsEnabled = true;
        }
protectedoverridevoid Seed(ApplicationDbContext context)
        {
            var manager = new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(
new ApplicationDbContext()));
for (int i = 0; i < 4; i++)
            {
                var user = new ApplicationUser()
                {
                    UserName = string.Format("User{0}", i.ToString())
                };
                manager.Create(user, string.Format("Password{0}", i.ToString()));
            }
        }
    }
}

 

Now that we have added code to seed the user table, we run the following command from the console:

Enable-Migration Init Command:
Add-Migration Init

 

When the command is finished running (it can take a few seconds) our console window looks like this:

Console Output from Enable-Migration Init:

console-add-migration-init

The previous Add-Migration Init command created the scaffolding code necessary to create the database, but to this point. If we look in the Migrations folder now, we will see a couple new files added containing that code. However, nothing we have done to this point has created or modified an actual database. All the preceding has been set up.

Create/Update the Seeded Database using the Update-Database Command

With all that done, run the following command from the console:

Update-Database

 

When the command finishes, your console should look like this:

Console Output from Update-Database:

console-update-database

If all went well, we now see the database has been re-created with all the expected tables as before. Additionally, if we SELECT * FROM dbo.AspNetUsers we find that we now have four test users:

Query Result from dbo.AspNetUsers Table:

query-aspnetusers-table

Now that we have a basic migration strategy in place, let's take a look at extending the elementary ApplicationUser class to incorporate some additional data fields.

Extending the IdentityModel Class with Additional Properties

Under the ASP.NET new Identity Model, it is easier than before to extend the basic user representation to include arbitrary fields suitable for our application management. For example, Let's assume we would like our user information to include an email address, as well as full first and last names. We can add properties for these items to the ApplicationUser class, and then update the Controllers, ViewModels, and Views which rely on Application user for registration and such.

First, let's go back to the ApplicationUser class and add the properties we want:

using Microsoft.AspNet.Identity.EntityFramework;
// Add this to bring in Data Annotations:
using System.ComponentModel.DataAnnotations;
namespace DbMigrationExample.Models
{
publicclass ApplicationUser : IdentityUser
    {
        [Required]
publicstring FirstName { get; set; }
        [Required]
publicstring LastName { get; set; }
        [Required]
publicstring Email { get; set; }
    }
publicclass ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }
    }
}

In the above, we added our three new properties to the ApplicationUser class, and also added the [Required] attribute. To do so, we need to add System.ComponentModel.DataAnnotations to our using statements at the top of the class file.

Update the Register Method of the AccountController

We also need to update the Register method on the AccountController. Currently, the code creates an instance of ApplicationUser and sets only the UserName property:

Currently, Only the UserName Property is Set:
var user = new ApplicationUser() { UserName = model.UserName };

 

We need to add the following (the code following the comment) to make our controller work properly:

Register Method of AccountController Updated to Set the New User Properties:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
    {
// Add the following to populate the new user properties
// from the ViewModel data:
        var user = new ApplicationUser() 
        { 
            UserName = model.UserName, 
            FirstName = model.FirstName,
            LastName = model.LastName,
            Email = model.Email
        };
        var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
        {
            await SignInAsync(user, isPersistent: false);
return RedirectToAction("Index", "Home");
        }
else
        {
            AddErrors(result);
        }
    }
// If we got this far, something failed, redisplay form
return View(model);
}

 

Update the Register ViewModel

Now that we have added the new properties to our ApplicationUser class, we also need to provide a way for the user to input the values during the registration process. If we open the AccountViewModels.cs file, we see a number of ViewModel classes defined. At the bottom is the RegisterViewModel class. It currently looks like this:

The Default RegisterViewModel Class:
publicclass RegisterViewModel
{
    [Required]
    [Display(Name = "User name")]
publicstring UserName { get; set; }
    [Required]
    [StringLength(100, ErrorMessage = 
"The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
publicstring Password { get; set; }
    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage = 
"The password and confirmation password do not match.")]
publicstring ConfirmPassword { get; set; }
}

 

We want to add our new properties, so we modify it as follows:

Modified RegisterViewModel Class:
publicclass RegisterViewModel
{
    [Required]
    [Display(Name = "User name")]
publicstring UserName { get; set; }
    [Required]
    [StringLength(100, ErrorMessage = 
"The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
publicstring Password { get; set; }
    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage = 
"The password and confirmation password do not match.")]
publicstring ConfirmPassword { get; set; }
    [Required]
    [Display(Name = "First name")]
publicstring FirstName { get; set; }
    [Required]
    [Display(Name = "Last name")]
publicstring LastName { get; set; }
    [Required]
    [Display(Name = "Email")]
publicstring Email { get; set; }
}

 

Update the Register View

Now that we have that taken care of, we need to also modify the Register.cshtml View to match. In the folder Views => Account open the Register.cshtml file. It should look like this:

The Default Register.cshtml File:
@model DbMigrationExample.Models.RegisterViewModel
@{
    ViewBag.Title = "Register";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("Register", "Account", 
    FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()
<h4>Create a new account.</h4>
<hr />
    @Html.ValidationSummary()
<div class="form-group">
        @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
        @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
        @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Register" />
</div>
</div>
}
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

 

Add the new properties after the existing form-group element for "ConfirmPassword" as follows:

Modified Register.cshml File:
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("Register", "Account", 
    FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()
<h4>Create a new account.</h4>
<hr />
    @Html.ValidationSummary()
<div class="form-group">
        @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
        @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
        @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
</div>
</div>
// Add new properties here:
<div class="form-group">
        @Html.LabelFor(m => m.FirstName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.TextBoxFor(m => m.FirstName, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
        @Html.LabelFor(m => m.LastName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.TextBoxFor(m => m.LastName, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
        @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Register" />
</div>
</div>
}
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

 

Updating the Database to Reflect Modified Entity Classes

So far, we have modified one of our Data model Entity classes – namely, the ApplicationUser class. In our application, EF is mapping this class to the dbo.AspNetUsers table in our backend. We need to run Migrations again to update things. Before we do that though, there is one more thing we need to do. Our seed method is no longer in sync with what our classes (and shortly, our back-end tables) require. We need to add values for the new FirstName, LastName, and Email properties to our user Seed data:

Updated Seed method:
protectedoverridevoid Seed(ApplicationDbContext context)
{
    var manager = new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(
new ApplicationDbContext()));
for (int i = 0; i < 4; i++)
    {
        var user = new ApplicationUser()
        {
            UserName = string.Format("User{0}", i.ToString()),
// Add the following so our Seed data is complete:
            FirstName = string.Format("FirstName{0}", i.ToString()),
            LastName = string.Format("LastName{0}", i.ToString()),
            Email = string.Format("Email{0}@Example.com", i.ToString()),
        };
        manager.Create(user, string.Format("Password{0}", i.ToString()));
    }
}

 

Now, if we were to run the Update-Database command again, the changes to our entity objects will be reflected in the dbo.AspNetUsers table schema, but our seed data will not be updated, because Entity Framework doesn't like to do things which will cause data loss. While there are ways to make this happen, they are beyond the scope of this article. Instead, we will manually delete the database, and then run Update-Database again. However, since EF thinks that we have existing data from our previous migration, we have to use Update-Database -force.

Once we have deleted the database and run the Update-Database –force command, our console output should look like this:

Console Output Update-Database –force After Deleting Database:

console-update-database-force

Quickly re-running our query shows that indeed, the new fields have been added to our table, and the test data populated:

Query Result from dbo.AspNetUsers Table:

query-aspnetusers-table-updated

User Registration with Updated Registration Form

Now that we have updated our Registration.cshtml View, Controller method, ViewModel, and Database, when we run the application and go to register, we see the updated Registration form:

The Updated Registration Form:

site-registration-with-new-properties

Once we complete the form and hit the Register button, we are logged in successfully, wit our additional data persisted in the database, ready to be used in our application.

Logging in Using Seed Data

Alternatively, we can also log out, and log in as one of our test users by clicking the "Log in" link:

Logging in with Test Data:

log-in-as-test-user

Successful Log-in:

successfully-logged-in-as-test-user

Only the Beginning

The updates to ASP.NET MVC 5 are numerous, and cool. In this article I have kept it really basic, and only scratched the surface of the updated Account management possibilities. The ASP.NET and MVC team has made it easy to do things like add role-based identity management, as well as external log-ins from social networks such as Google +, Facebook, and Twitter.

In my next post, I will take a closer look at creating a role-based identity management model for administering a "closed" site.

Additional Resources and Items of Interest

Keep Nuget Packages Out of Source Control with Nuget Package Manager Restore

$
0
0

NuGet_project_logo.svgNuget is cool.

What is not cool is when you push a project to source control and include a ton of installed packages, bulking up your repo with stuff that could be easily pulled in via Nuget by anyone cloning your project. However, what was equally irritating was having to include instructions for what packages need to be installed, if they were excluded from the source repo.

Enter Nuget Package Restore.

 

Allow Visual Studio to Restore Nuget Packages During Build

Before you can avail yourself of Nuget Package Restore, we need to explicitly allow this behavior on our machine. This is required on a per-machine basis before Nuget can restore packages for a solution automagically, and can be set in Tools => Options => Package Manager:

Visual Studio Options Menu:

vs-tools-options-menu

Package Manager Settings Dialog:

package-manager-settings

Now that our machine has been told it is ok to restore Nuget Packages automatically, we need to enable this behavior in our specific solution as well.

Enable Nuget Package Restore for the Solution

To enable Nuget Package Restore in our specific solution, right-click on Solution Explorer => Your Solution => Enable Package Restore:

Enabling Nuget Package Restore for a Specific VS Solution:

enable-package-restore-in-solution

This may take a few seconds. When VS is done processing, an examination of Solution Explorer reveals a new folder at the root solution level:

Files Added to Solution by Nuget Package Restore:nuget-package-restore-files-added

 

Notice the NuGet.exe file in there? This file is required when, for example, someone clones your project from source and attempts to build with Nuget Package Restore enabled. Therefore, it needs to be committed to source control.

We also now want to exclude all the package files from source by editing our .gitignore file (I am assuming Git as the source control provider, but the principle here applies to whichever provider you are using – you need to tell your version control system what to include in the repository and what to keep out).

Edit .gitignore to Exclude Packages and Include NuGet.exe

A typical ASP.NET MVC 5 project created from the standard VS 2013 template will contain (as of this writing) 161 files from various Nuget packages included as part of the default solution. This is some significant bulk. Using the default .gitignore file (or any of the most common in use for Visual Studio/C# projects) the total number of files which will tend to be included as part of the project repo numbers over 200. Note this is after excluding all the binaries and VS housekeeping files. In other words, nearly 75% of the common files in a VS 2013 ASP.NET MVC 5 solution consist of Nuget packages, which can now be restored automatically instead of pushed up to our repo.

To cut down the bulk, lets modify our .gitignore as follows. Note, your .gitignore (or .hgignore, or .tfsignore, what have you) will most likely look a little different than mine. That's okay, because we're only going to do a few small tweaks to the file – keep the rest the same.

Ignore Packages Folder

Open your .gitignore file and add the following line to exclude the packages folder from source:

Ignore Packages Folder in .gitignore:
packages*/

 

Make an Exception to Include NuGet.exe

Most likely, your .gitignore file already excludes files with the .exe extension. We may want to make an exception for Nuget.exe, since it will be needed to restore packages by anyone cloning our repo:

Look through your .gitignore until you find the following:

*.exe

 

Then add this right AFTER it (the order here is important):

*.exe
!NuGet.exe

 

The above tells Git to ignore .exe files, but to make an exception for NuGet.exe.

The step above is optional, since someone cloning your solution could simply Enable Nuget Package Restore for the solution, at which point the NuGet.exe would be installed. However, this saves the step.

Inform Potential Consumers of Your Code

Now, we no longer need to include all the installed Nuget package files in our source repo. However, we should probably add a blurb to the README.txt file (or otherwise inform consumers of our code) letting users know that they will need to perform the VS configuration needed to allow Nuget to restore packages as described in the first step in this article.

Additional Resources and Items of Interest

Extending Identity Accounts and Implementing Role-Based Authentication in ASP.NET MVC 5

$
0
0

5555555-500In a previous post, we took a quick look at extending Identity Accounts in the context of the new Identity system under ASP.NET MVC 5. We also examined the basics of Entity Framework Code-First Migrations. If you haven't read that article, you might want to do so now, so we don't have to repeat some of the general ideas explained there.

 

Image by: Elif Ayiter | Some Rights Reserved

For the purpose of this post, we are going to look at a implementing relatively simple role-based authentication and identity management for an ASP.NET MVC 5 web application. The examples used will be deliberately simplified, and while they will effectively illustrate the basics of setting up role-based identity management, I can promise that the implementation here will lack certain things we would like to see in a production project (such as complete exception handling). Also, production application modeling would likely look a little different based upon business needs.

In other words, I'm keeping it simple, much like we ignored the effects of friction way back in high school physics.

That said, there's a lot to cover. The article is not as long as it seems, because I am including some large code samples, and images to illustrate what's going on. The complete project source code for the project discussed in this article can be found at my Github Repo.

Download the Source Code

The complete project source code for this article is available at my Gihub Repo. NOTE: You will need to enable Nuget Package Restore in order to build the project properly. 

Basic Hypothetical Application Requirements

We will assume our identity management needs to do the following for a Line-of-Business web application used primarily by internal users or others suitably authorized by management. The application will have a minimal public-facing interface, and will require all users to log-in to access even minimal functionality (it is easy to extend what we will be doing to include public users, but this is derived from an actual application I needed to create rather quickly at work).

  • User accounts must be created by one or more Admin-level users. "Registration" as we know it from the ASP.NET Membership paradigm, does not exists (anonymous users cannot register and create accounts from the public site).
  • User Identity accounts will be extended to include an email address, and first/last names
  • For our purposes, there will be at least three Roles; Administrator (full access to everything), Editor (can perform most business functions of the application, but cannot access admin functions such as account management), and Read-Only User (what the name implies).
  • Each user may be a member of zero or more roles. Multiple roles will have the access rights of all the roles combines.
  • To keep things simple, roles are independently defined, and are not members of other roles.
  • All application roles are pre-defined. There is no administrative creation of roles. Also, Role permissions are integral to the application, and not manageable by administrators (this is for simplicity at this point).
  • Anonymous access to the site is not allowed, except to the log-in portal.
  • There will be no use of external log-ins or OAuth (code for this is included as part of the default MVC project. We will remove it to keep things clean).

Getting Started – Stripping Unneeded Items from the MVC Project Template

While we could start with what we created in the previous article on Extending Identity Accounts, we will be re-arranging things sufficiently it will be cleaner for our purposes to start fresh. Create a new ASP.NET MVC project in Visual Studio. Before we do anything else, let's clear out some unneeded clutter so we are left with only what we need.

We are going to be removing a bunch of code related to managing external log-ins, as well as clearing out some of the extraneous comments included with the default project (which to me, just add noise to our code).

We will start with the AccountController.

Simplifying AccountController – Remove the Clutter

There are a number of m on AccountController related to External Logins which we don't need. If you examine AccountController carefully, you can go through and delete the code for the following methods:

  • Dissociate()
  • ExternalLogin()
  • ExternalLoginCallback()
  • LinkLogin()
  • LinkLoginCallback()
  • ExternalLoginConfirmation()
  • ExternalLoginFailure()
  • RemoveAccountList()

Additionally, if you look closely, there is a code #region (I know. I hate #region and think it should be done away with) for helpers. From here, we can delete the following items:

  • The member constant XsrfKey
  • The entire class ChallengeResult

At this point, we can also right-click in the code file and select Remove and Sort Usings to clear out some of the unneeded clutter here as well.

At this point, our AccountController.cs file should contain the following methods (reduced to simple stubs here for brevity – we'll get to the code shortly:

The Cleaned Up AccountController.cs File (Stubs Only - Code Hidden for Brevity):
using AspNetRoleBasedSecurity.Models;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Owin.Security;
using System.Threading.Tasks;
using System.Web;
using System.Web.Mvc;
namespace AspNetRoleBasedSecurity.Controllers
{
    [Authorize]
publicclass AccountController : Controller
    {
public AccountController()
            : this(new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(new ApplicationDbContext())))
        {
        }
public AccountController(UserManager<ApplicationUser> userManager)
        {
            UserManager = userManager;
        }
public UserManager<ApplicationUser> UserManager { get; privateset; }
        [AllowAnonymous]
public ActionResult Login(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
return View();
        }
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
// . . . Code Hidden for brevity
        }
        [AllowAnonymous]
public ActionResult Register()
        {
return View();
        }
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
        {
// . . . Code Hidden for brevity
        }
public ActionResult Manage(ManageMessageId? message)
        {
// . . . Code Hidden for brevity
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
public async Task<ActionResult> Manage(ManageUserViewModel model)
        {
// . . . Code Hidden for brevity
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
public ActionResult LogOff()
        {
            AuthenticationManager.SignOut();
return RedirectToAction("Index", "Home");
        }
protectedoverridevoid Dispose(bool disposing)
        {
// . . . Code Hidden for brevity
        }
        #region Helpers
private IAuthenticationManager AuthenticationManager
        {
// . . . Code Hidden for brevity
        }
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
        {
// . . . Code Hidden for brevity
        }
privatevoid AddErrors(IdentityResult result)
        {
// . . . Code Hidden for brevity
        }
privatebool HasPassword()
        {
// . . . Code Hidden for brevity
        }
publicenum ManageMessageId
        {
// . . . Code Hidden for brevity
        }
private ActionResult RedirectToLocal(string returnUrl)
        {
// . . . Code Hidden for brevity
        }
        #endregion
    }
}

 

Remove Unneeded Views

Along with the unnecessary Controller methods we just removed, we can also remove the unnecessary views related to external logins. If we open the Views => Account folder in Solution Explorer, we find we can delete the highlighted views below from our project:

Solution Explorer – Remove Unneeded Views:

solution-explorer-remove-unneeded-views

Now that the totally unnecessary views are out of the way, let's remove the External Log-in code from the remaining views as well.

Clean Up Account-Related Views

There is some remaining clutter to clear out of our Account-related views. We don't want dead-end links on our site, and we want to keep only relevant code in our views.

We can start with the Login.cshtml file, which contains a section related to creating an external log-in from various social networks (highlighted in yellow).

Login.cshtml - Remove Social Network Login Option:
@{
    ViewBag.Title = "Log in";
}
<h2>@ViewBag.Title.</h2>
<div class="row">
<div class="col-md-8">
<section id="loginForm">
            @using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
            {
                @Html.AntiForgeryToken()
<h4>Use a local account to log in.</h4>
<hr />
                @Html.ValidationSummary(true)
<div class="form-group">
                    @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
                        @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
                        @Html.ValidationMessageFor(m => m.UserName)
</div>
</div>
<div class="form-group">
                    @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
                        @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
                        @Html.ValidationMessageFor(m => m.Password)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<div class="checkbox">
                            @Html.CheckBoxFor(m => m.RememberMe)
                            @Html.LabelFor(m => m.RememberMe)
</div>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Log in" class="btn btn-default" />
</div>
</div>
<p>
                    @Html.ActionLink("Register", "Register") if you don't have a local account.
</p>
            }
</section>
</div>
<div class="col-md-4">
<section id="socialLoginForm">
            @Html.Partial("_ExternalLoginsListPartial", new { Action = "ExternalLogin", ReturnUrl = ViewBag.ReturnUrl })
</section>
</div>
</div>
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

 

In the above, the section highlighted in yellow can be deleted.

Next, let's remove similar External Login functionality from the Manage.cshtml file (highlighted in yellow, again):

Manage.cshtml – Remove External Login Items:
@using AspNetRoleBasedSecurity.Models;
@using Microsoft.AspNet.Identity;
@{
    ViewBag.Title = "Manage Account";
}
<h2>@ViewBag.Title.</h2>
<p class="text-success">@ViewBag.StatusMessage</p>
<div class="row">
<div class="col-md-12">
        @if (ViewBag.HasLocalPassword)
        {
            @Html.Partial("_ChangePasswordPartial")
        }
else
        {
            @Html.Partial("_SetPasswordPartial")
        }
<section id="externalLogins">
            @Html.Action("RemoveAccountList")
            @Html.Partial("_ExternalLoginsListPartial", new { Action = "LinkLogin", ReturnUrl = ViewBag.ReturnUrl })
</section>
</div>
</div>
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

 

As before, the highlighted area can be safely removed.

Remove Unneeded Model Classes

As with the previous sections, there are unneeded model classes we can safely dispose of in order to clean up our project. If we expand the Models folder in Solution Explorer, we find there is a single code file, AccountViewModels.cs,  containing several ViewModel classes related to Identity Management. Review the file carefully, and delete the ExternalLogInConfirmationViewModel item highlighted in yellow below:

Account View Models File – Remove Unneeded Classes:

 

using System.ComponentModel.DataAnnotations;
namespace AspNetRoleBasedSecurity.Models
{
publicclass ExternalLoginConfirmationViewModel
    {
        [Required]
        [Display(Name = "User name")]
publicstring UserName { get; set; }
    }
publicclass ManageUserViewModel
    {
        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Current password")]
publicstring OldPassword { get; set; }
        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "New password")]
publicstring NewPassword { get; set; }
        [DataType(DataType.Password)]
        [Display(Name = "Confirm new password")]
        [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
publicstring ConfirmPassword { get; set; }
    }
publicclass LoginViewModel
    {
        [Required]
        [Display(Name = "User name")]
publicstring UserName { get; set; }
        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
publicstring Password { get; set; }
        [Display(Name = "Remember me?")]
publicbool RememberMe { get; set; }
    }
publicclass RegisterViewModel
    {
        [Required]
        [Display(Name = "User name")]
publicstring UserName { get; set; }
        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
publicstring Password { get; set; }
        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
publicstring ConfirmPassword { get; set; }
    }
}

 

Extending the Identity Management Models and View Models

As we have seen, the Model classes used by our application to manage identity and authorization are contained in the IdentityModels.cs file. Additionally, there are Identity-related ViewModel classes defined in the AccountViewModels.cs file used to manage the transfer of identity data between our views and controllers.

Important to note here that we would really like to get all of our model definitions correct before we run the application and create any new user accounts, or register using the normal (and soon-to-be-removed) MVC "registration" mechanism. We are going to use Entity Framework Migrations and Code-First to do the Database heavy-lifting for us. While it is not terribly difficult to update our models later (and hence, the database, through EF migrations), it is cleaner and smoother to get it right up front. 

In order to conform to our project specifications, one of the first things we need to do is extend the default ApplicationUser class to include Email, LastName, and FirstName properties. Open the IdentityModels.cs file. Currently, the code should look like this:

The Default IdentityModels File with Emply ApplicationUser Stub:
using Microsoft.AspNet.Identity.EntityFramework;
namespace AspNetRoleBasedSecurity.Models
{
publicclass ApplicationUser : IdentityUser
    {
    }
publicclass ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }
    }
}

 

In this step, we are going to extend our ApplicationUser class to include the properties required by our application specification. Also, we will add an IdentityManager class in which we consolidate our user and role management functions. We'll discuss how all this works in a moment. For now, add the following code to the IdentityModels.cs code file (note we have added some new using statements at the top as well):

Modified IdentityModels.cs File
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
namespace AspNetRoleBasedSecurity.Models
{
publicclass ApplicationUser : IdentityUser
    {
        [Required]
publicstring FirstName { get; set; }
        [Required]
publicstring LastName { get; set; }
        [Required]
publicstring Email { get; set; }
    }
publicclass ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }
    }
publicclass IdentityManager
    {
publicbool RoleExists(string name)
        {
            var rm = new RoleManager<IdentityRole>(
new RoleStore<IdentityRole>(new ApplicationDbContext()));
return rm.RoleExists(name);
        }
publicbool CreateRole(string name)
        {
            var rm = new RoleManager<IdentityRole>(
new RoleStore<IdentityRole>(new ApplicationDbContext()));
            var idResult = rm.Create(new IdentityRole(name));
return idResult.Succeeded;
        }
publicbool CreateUser(ApplicationUser user, string password)
        {
            var um = new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(new ApplicationDbContext()));
            var idResult = um.Create(user, password);
return idResult.Succeeded;
        }
publicbool AddUserToRole(string userId, string roleName)
        {
            var um = new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(new ApplicationDbContext()));
            var idResult = um.AddToRole(userId, roleName);
return idResult.Succeeded;
        }
publicvoid ClearUserRoles(string userId)
        {
            var um = new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(new ApplicationDbContext()));
            var user = um.FindById(userId);
            var currentRoles = new List<IdentityUserRole>();
            currentRoles.AddRange(user.Roles);
foreach(var role in currentRoles)
            {
                um.RemoveFromRole(userId, role.Role.Name);
            }
        }
    }
}

 

Yeah, I know. There is room for some refactoring here.We'll ignore that for now. In the above, we have extended ApplicationUser to include our new required properties, and added the IdentityManager class, which includes the methods required to create new users, and add/remove users from available roles.

We have also decorated our new ApplicationUser properties with the [Required] data annotation, which will be reflected both in our database (nulls will not be allowed) and our Model Validation for our views.

SIDE NOTE, REDUX: I am utilizing the methods available directly within the Microsoft.AspNet.Identity and Microsoft.AspNet.Identity.EntityFramework namespaces. I am content to let the ASP.NET team invent and provide the best practices for managing application security. Therefore, in the context of this application, I am not inventing my own. I strongly recommend you do as well. It is easy to spot ways to manage some of the Account/Identity stuff (including data persistence) which appear more direct or easier. I concluded that the team thought all this through more effectively than I can. Therefore, while we are creating an authorization management structure here, we are doing so using the core implementation provided by people who know better than we do.

Extending Account Management ViewModels

Now that we have expanded upon our basic Identity Models, we need to do the same with our Account ViewModels. ViewModels essentially represent a data exchange mechanism between our views and Controllers. Our goal here is to provide our Account management views with precisely the information required to perform the task at hand, and no more.

Also of note is that for the purpose of our presentation layer, I am not pushing User or Role id's out onto the page or the backing html. Instead I am relying on the unique nature of the Username and Role names to look up the proper Id value server-side.

Currently, before we do anything, our AccountVeiwModels.cs file looks like this:

The AccountViewModels.cs File Before Modification:
using System.ComponentModel.DataAnnotations;
namespace AspNetRoleBasedSecurity.Models
{
publicclass ManageUserViewModel
    {
        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Current password")]
publicstring OldPassword { get; set; }
        [Required]
        [StringLength(100, ErrorMessage = 
"The {0} must be at least {2} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "New password")]
publicstring NewPassword { get; set; }
        [DataType(DataType.Password)]
        [Display(Name = "Confirm new password")]
        [Compare("NewPassword", ErrorMessage = 
"The new password and confirmation password do not match.")]
publicstring ConfirmPassword { get; set; }
    }
publicclass LoginViewModel
    {
        [Required]
        [Display(Name = "User name")]
publicstring UserName { get; set; }
        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
publicstring Password { get; set; }
        [Display(Name = "Remember me?")]
publicbool RememberMe { get; set; }
    }
publicclass RegisterViewModel
    {
        [Required]
        [Display(Name = "User name")]
publicstring UserName { get; set; }
        [Required]
        [StringLength(100, ErrorMessage = 
"The {0} must be at least {2} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
publicstring Password { get; set; }
        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = 
"The password and confirmation password do not match.")]
publicstring ConfirmPassword { get; set; }
    }
}

 

We are going to expand on this significantly. In fact, it may seem, redundantly. For, with a few minor differences, it might appear that one or two of the ViewModels in the following code are near-duplicates, and possible candidates for a refactoring into a single class. However, I decided that the purpose of the ViewModel is to represent the specific data required by a specific view. While in some cases these appear to be the same, that may change. I concluded that it is better, so far as Views and ViewModels go, to have some potential duplication, but preserve the ability of each view to evolve independently should the need arise, without having to fuss with the impact on other views dependent on the same ViewModel.

Modify the code above as follows (or simply replace it with the following). We'll take a closer look at the functionality in a moment, when we get to the Controller implementation:

Modified Code for AccountViewModels.cs File:
using System.ComponentModel.DataAnnotations;
// New namespace imports:
using Microsoft.AspNet.Identity.EntityFramework;
using System.Collections.Generic;
namespace AspNetRoleBasedSecurity.Models
{
publicclass ManageUserViewModel
    {
        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Current password")]
publicstring OldPassword { get; set; }
        [Required]
        [StringLength(100, ErrorMessage = 
"The {0} must be at least {2} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "New password")]
publicstring NewPassword { get; set; }
        [DataType(DataType.Password)]
        [Display(Name = "Confirm new password")]
        [Compare("NewPassword", ErrorMessage = 
"The new password and confirmation password do not match.")]
publicstring ConfirmPassword { get; set; }
    }
publicclass LoginViewModel
    {
        [Required]
        [Display(Name = "User name")]
publicstring UserName { get; set; }
        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
publicstring Password { get; set; }
        [Display(Name = "Remember me?")]
publicbool RememberMe { get; set; }
    }
publicclass RegisterViewModel
    {
        [Required]
        [Display(Name = "User name")]
publicstring UserName { get; set; }
        [Required]
        [StringLength(100, ErrorMessage = 
"The {0} must be at least {2} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
publicstring Password { get; set; }
        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = 
"The password and confirmation password do not match.")]
publicstring ConfirmPassword { get; set; }
// New Fields added to extend Application User class:
        [Required]
        [Display(Name = "First Name")]
publicstring FirstName { get; set; }
        [Required]
        [Display(Name = "Last Name")]
publicstring LastName { get; set; }
        [Required]
publicstring Email { get; set; }
// Return a pre-poulated instance of AppliationUser:
public ApplicationUser GetUser()
        {
            var user = new ApplicationUser()
            {
                UserName = this.UserName,
                FirstName = this.FirstName,
                LastName = this.LastName,
                Email = this.Email,
            };
return user;
        }
    }
publicclass EditUserViewModel
    {
public EditUserViewModel() { }
// Allow Initialization with an instance of ApplicationUser:
public EditUserViewModel(ApplicationUser user)
        {
this.UserName = user.UserName;
this.FirstName = user.FirstName;
this.LastName = user.LastName;
this.Email = user.Email;
        }
        [Required]
        [Display(Name = "User Name")]
publicstring UserName { get; set; }
        [Required]
        [Display(Name = "First Name")]
publicstring FirstName { get; set; }
        [Required]
        [Display(Name = "Last Name")]
publicstring LastName { get; set; }
        [Required]
publicstring Email { get; set; }
    }
publicclass SelectUserRolesViewModel
    {
public SelectUserRolesViewModel() 
        {
this.Roles = new List<SelectRoleEditorViewModel>();
        }
// Enable initialization with an instance of ApplicationUser:
public SelectUserRolesViewModel(ApplicationUser user) : this()
        {
this.UserName = user.UserName;
this.FirstName = user.FirstName;
this.LastName = user.LastName;
            var Db = new ApplicationDbContext();
// Add all available roles to the list of EditorViewModels:
            var allRoles = Db.Roles;
foreach(var role in allRoles)
            {
// An EditorViewModel will be used by Editor Template:
                var rvm = new SelectRoleEditorViewModel(role);
this.Roles.Add(rvm);
            }
// Set the Selected property to true for those roles for 
// which the current user is a member:
foreach(var userRole in user.Roles)
            {
                var checkUserRole = 
this.Roles.Find(r => r.RoleName == userRole.Role.Name);
                checkUserRole.Selected = true;
            }
        }
publicstring UserName { get; set; }
publicstring FirstName { get; set; }
publicstring LastName { get; set; }
public List<SelectRoleEditorViewModel> Roles { get; set; }
    }
// Used to display a single role with a checkbox, within a list structure:
publicclass SelectRoleEditorViewModel
    {
public SelectRoleEditorViewModel() {}
public SelectRoleEditorViewModel(IdentityRole role)
        {
this.RoleName = role.Name;
        }
publicbool Selected { get; set; }
        [Required]
publicstring RoleName { get; set;}
    }
}

 

Extending the Account Controller

Now that we have our Models and ViewModels mostly in place, let's look at how it all comes together in the Controller. Our current AccountController defines Controller Actions for the following:

  • Register (Essentially creates a new user)
  • Manage (Essentially allows the user to change their password)
  • Login
  • Log Off

Also, the above methods are focused upon allowing anonymous users to self-register, and create their own user account.

We are not planning to allow self-registration, and our requirements establish that user accounts are to be created by an administrator. Also, we have extended our ApplicationUser model to include some additional properties. From a functional perspective, we would like to se the following behavior implemented:

  • View a list of user accounts (Index), with links to various relevant functionality (Edit, Roles, Etc.)
  • Create a new User (We will co-opt the "Register" method for this, but we will extend it significantly).
  • Edit a User (Administrators need to be able to edit user accounts, assign roles, and such)
  • Delete a User (We want to be able to remove User accounts (or at least render them active or inactive)
  • Assign Roles to a User
  • Login
  • Log Off

Before we proceed, understand that there are countless possible major and minor variations we could consider for the above. I chose an application model which was simple, and for our purposes, rather arbitrary. For example, it could be your application requirements allow for self-registration of anonymous users into a default Role of some sort. The model I represent here is by purpose rather limited in scope, as we are trying to see concepts without getting too distracted by a complex implementation. I leave to you to expand from here into more complex (and more useful to you) application models.

In keeping with the above, I have added the [Authorize(Roles = "Admin")] attribute to all of the administrative methods, with the assumption that our administrative role will be called (wait for it . . .) "Admin." More on this later too.

Where was I?

Oh, yeah. So, looking at the functional needs in the list above, I am going to modify my AccountController to incorporate the above items. As mentioned parenthetically, I am simply going to co-opt the Register controller method and use it for what should probably be named Create (out of laziness at this point!).

Modifying The AccountController Register Method (Create User)

First, we will look at modifying our existing Register method(s) to accommodate our new ApplicationUser properties. We want to be able to create a new ApplicationUser in our View, and then persist the new record in our database.

Modified Register Method:
[Authorize(Roles = "Admin")]
public ActionResult Register()
{
return View();
}
[HttpPost]
[Authorize(Roles = "Admin")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
    {
        var user = model.GetUser();
        var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
        {
return RedirectToAction("Index", "Account");
        }
    }
// If we got this far, something failed, redisplay form
return View(model);
}

 

In the above, we have not changed much except using the handy GetUser() method defined on our RegisterViewModel to retrieve an instance of ApplicationUser, populated and ready to persist in our database. Also, we are redirecting to a new Index method we will define momentarily on our AccountController.

Adding The AccountController Index Method (View List of Users)

Previously, the AccountController did not have an Index method. We need a way for our Administrators to view a list of users of our application, and access the functionality to edit, assign roles, and delete. Once again, accessing the user account data is an admin function, so we have added the [Authorize] attribute as well.

The New Index Method:
[Authorize(Roles = "Admin")]
public ActionResult Index()
{
    var Db = new ApplicationDbContext();
    var users = Db.Users;
    var model = new List<EditUserViewModel>();
foreach(var user in users)
    {
        var u = new EditUserViewModel(user);
        model.Add(u);
    }
return View(model);
}

 

Our Index method uses a List<EditUserViewModel> for now, as it contains all the information needed for display in our list. Contrary to what I said above, I have re-used a ViewModel here. I should probably fix that, but you can make your own decision on this point.

Notice that instead of performing the tedious mapping of properties from ApplicationUser instance to each EditUserViewModel within this method, I simply pass the ApplicationUser instance into the overridden constructor on EditUserViewModel. Our Index.cshtml View will expect a List<EditUserViewModel> as the model for display.

Adding The AccountController Edit Method

We have added an Edit method to facilitate administrative updating of User account data. There are some details to pay attention to in the Edit method implementation, at least in my version. First, while the method still accepts a parameter named id, what we will actually be passing to the method when a request is routed here will be a UserName. Why? I decided to follow the lead of the ASP.NET team on this. They are not passing User Id's (which are GUID's) out into the public HTML, so neither will I.

Also, by design and constraint, the UserName is unique in our database, and will already be a semi-public piece of information. Just something to keep in mind – the id parameter for public Account Action methods will be the User (or, as the case may be, Role) name, and not an integer. Lastly, I didn't want to add a whole new route just to rename a single route parameter which is serving essentially the same purpose as an int id.

That said, the following is the new Edit method, which will be used when an Administrator wishes to update user information:

The New Edit Method:
[HttpPost]
[Authorize(Roles = "Admin")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(EditUserViewModel model)
{
if (ModelState.IsValid)
    {
        var Db = new ApplicationDbContext();
        var user = Db.Users.First(u => u.UserName == model.UserName);
// Update the user data:
        user.FirstName = model.FirstName;
        user.LastName = model.LastName;
        user.Email = model.Email;
        Db.Entry(user).State = System.Data.Entity.EntityState.Modified;
        await Db.SaveChangesAsync();
return RedirectToAction("Index");
    }
// If we got this far, something failed, redisplay form
return View(model);
}

 

In the above, we use Linq to grab a reference to the specific user based on the UserName passed in as the id parameter in the first (GET) Edit method. We then populate an instance of EditUserViewModel by passing the ApplicationUser Instance to the constructor, and pass the ViewModel on to the Edit.cshtml View.

When the View returns our updated model to the second (POST) Edit method, we do much the same in reverse. We retrieve the User record from the database, then update with the model data, and save changes.

In our View, it will be important to remember that we cannot allow editing of the UserName property itself (at least, not under our current model, which considers the UserName to be an inviolate identifier).

Adding the AccountController Delete Method

We are adding a Delete (GET) method and a DeleteConfirmed (POST) method to the AccountController class. In my implementation, this method will actually delete the selected user from the database. You may decide instead to flag the database record as deleted, or implement some other method of managing unwanted user records.

You might also forego a delete method, and instead add a Boolean Inactive property to the ApplicationUser class, and manage Active/Inactive status through the Edit method discussed previously. Again, there are many permutations to the design model here. I went with the simplest for the sake of clarity.

The Delete method implementation here is straightforward so long as we remember, once again, that the id parameter passed to each of the two related methods below is actually a UserName.

The New Delete Methods:
[Authorize(Roles = "Admin")]
public ActionResult Delete(string id = null)
{
    var Db = new ApplicationDbContext();
    var user = Db.Users.First(u => u.UserName == id);
    var model = new EditUserViewModel(user);
if (user == null)
    {
return HttpNotFound();
    }
return View(model);
}
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
[Authorize(Roles = "Admin")]
public ActionResult DeleteConfirmed(string id)
{
    var Db = new ApplicationDbContext();
    var user = Db.Users.First(u => u.UserName == id);
    Db.Users.Remove(user);
    Db.SaveChanges();
return RedirectToAction("Index");
}

 

As we can see, the DeleteConfirmed method is decorated with a HttpPost attribute, and an ActionName attribute "Delete" which means POST requests routed to Delete will be routed here. Both methods use the UserName passed as the id parameter to look up the appropriate ApplicationUser in the database.

Once again, in contrast to what I said earlier, I re-used the EditUserViewModel to pass to the Delete.cshtml View.

Adding the UserRoles Method to the AccountController

Lastly, we add the UserRoles method pair. This is where we manage assignment of user accounts to various application roles.

The implementation here looks relatively simple, and pretty similar to the other controller methods we have examined so far. However, under the hood in the SelectUserRolesViewModel and in the IdentityManager class, there is a lot going on. First, the code:

The New UserRoles Method(s):
[Authorize(Roles = "Admin")]
public ActionResult UserRoles(string id)
{
    var Db = new ApplicationDbContext();
    var user = Db.Users.First(u => u.UserName == id);
    var model = new SelectUserRolesViewModel(user);
return View(model);
}
[HttpPost]
[Authorize(Roles = "Admin")]
[ValidateAntiForgeryToken]
public ActionResult UserRoles(SelectUserRolesViewModel model)
{
if(ModelState.IsValid)
    {
        var idManager = new IdentityManager();
        var Db = new ApplicationDbContext();
        var user = Db.Users.First(u => u.UserName == model.UserName);
        idManager.ClearUserRoles(user.Id);
foreach (var role in model.Roles)
        {
if (role.Selected)
            {
                idManager.AddUserToRole(user.Id, role.RoleName);
            }
        }
return RedirectToAction("index");
    }
return View();
}

 

As we can see above, and incoming GET request routed to the UserRoles method is handled similarly to those in previous methods. The UserName passed as the id parameter is used to retrieve the User record from the database, and then an instance of SelectUserRolesViewModel is initialized, passing the ApplicationUser instance to the constructor.

Here is where things get interesting. Let's take another look at the code for our SelectUserRolesViewModel from the AccountViewModels.cs file:

Code for SelectUserRolesViewModel – Revisited:
publicclass SelectUserRolesViewModel
{
public SelectUserRolesViewModel() 
    {
this.Roles = new List<SelectRoleEditorViewModel>();
    }
// Enable initialization with an instance of ApplicationUser:
public SelectUserRolesViewModel(ApplicationUser user) : this()
    {
this.UserName = user.UserName;
this.FirstName = user.FirstName;
this.LastName = user.LastName;
        var Db = new ApplicationDbContext();
// Add all available roles to the list of EditorViewModels:
        var allRoles = Db.Roles;
foreach(var role in allRoles)
        {
// An EditorViewModel will be used by Editor Template:
            var rvm = new SelectRoleEditorViewModel(role);
this.Roles.Add(rvm);
        }
// Set the Selected property to true for those roles for 
// which the current user is a member:
foreach(var userRole in user.Roles)
        {
            var checkUserRole = 
this.Roles.Find(r => r.RoleName == userRole.Role.Name);
            checkUserRole.Selected = true;
        }
    }
publicstring UserName { get; set; }
publicstring FirstName { get; set; }
publicstring LastName { get; set; }
public List<SelectRoleEditorViewModel> Roles { get; set; }
}

 

During initialization, we are populating a List<SelectRoleEditorViewModel>() with all the roles available in the application. First,this is a prime candidate for refactoring, as I am performing  data access within the object constructor (a general no-no). Second, SelectRoleEditorViewModel? What?

In my current implementation, we will see that the SelectUserRolesViewModel is passed to the UserRoles.cshtml view. We want to display the basic user details (so we know which user we are assigning roles to – always important to know), as well as a list of all available Roles. I have decided to facilitate role assignment using checkboxes, whereby roles are assigned to the user by checking one or more (or none!) checkboxes.

This is where the EditorViewModel comes in.

Let's revisit the code for SelectRoleEditorViewModel, which we defined in our AccountViewModels.cs file.

The SelectRoleEditorViewModel represents an individual role, and as we can see from the following code, includes a Boolean field used to indicate the Selected status for that role:

Code for SelectRoleEditorViewModel, Revisited:
publicclass SelectRoleEditorViewModel
{
public SelectRoleEditorViewModel() { }
public SelectRoleEditorViewModel(IdentityRole role)
    {
this.RoleName = role.Name;
    }
publicbool Selected { get; set; }
    [Required]
publicstring RoleName { get; set; }
}

 

This EditorViewModel will be used by a special View called an EditorTemplate, which we will look at shortly. For now, bear in mind that the SelectUserRolesViewModel contains a List of SelectRoleEditorViewModel objects (yes, the naming of these could be better and posed some challenges – I am open to suggestion here! For the moment, I try to think of them as "SelectUserRoles-ViewModel" and "SelectRole-EditorViewModel" if that helps).

That covers the modified or new items in our AccountController. Now let's look at our Views.

Basic Views for Role-Based Identity Management

We already have a few of the views we will need, we just need to modify them a bit. In addition, we need to add a few new ones. We'll start by modifying our existing views to suit our needs, beginning with the Register.cshmtl View.

Modifying the Register.cshtml View File

The Register view as it currently sits was designed to allow user self-registration. As we have co-opted the Register method on the AccountController for restricted administrative use, so we will co-opt the Register.cshtml View for our purposes.

Essentially, all we need to do is add some additional HTML and Razor-Syntax code to the file to accommodate the new properties we added to our ApplicationUser class:

Modifying the Register.cshtml File:
@model AspNetRoleBasedSecurity.Models.RegisterViewModel
@{
    ViewBag.Title = "Register";
}
<h2>@ViewBag.Title.</h2>
@using (Html.BeginForm("Register", "Account", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()
<h4>Create a new account.</h4>
<hr />
    @Html.ValidationSummary()
<div class="form-group">
        @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.TextBoxFor(m => m.UserName, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
        @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.PasswordFor(m => m.Password, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
        @Html.LabelFor(m => m.ConfirmPassword, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.PasswordFor(m => m.ConfirmPassword, new { @class = "form-control" })
</div>
</div>
// Add the LastName, FirstName, and Email Properties:
<div class="form-group">
        @Html.LabelFor(m => m.LastName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.TextBoxFor(m => m.LastName, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
        @Html.LabelFor(m => m.FirstName, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.TextBoxFor(m => m.FirstName, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
        @Html.LabelFor(m => m.Email, new { @class = "col-md-2 control-label" })
<div class="col-md-10">
            @Html.TextBoxFor(m => m.Email, new { @class = "form-control" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" value="Register" />
</div>
</div>
}

 

Next, we will create our edit view. To do this, we can right-click on the Edit Action method declaration in AccountController and let VS do the work for us:

Create View for the Edit Method:

add-view-edit-method

We next see the Add View Dialog. Choose the Edit template from the Template drop-down, and select the EditUserViewModel class from the Model Class drop-down. We already have a data context, so leave that blank.

Add View Dialog:

add-view-dialog

Repeat the process above for the Delete and Index methods. Choose the appropriate template for each (use the List template for the Index View, as we want to display a list of User Accounts), and use EditUserViewModel as the Model Class for both.

Fine-Tuning the Index View

We need to make a few minor changes to our index view.

Notice near the bottom, where the template has provided handy links for Edit, Details, and Delete. We will change the "Details" link to instead point to our UserRoles Action method. Also, we need to replace the commented out route parameters such that the Username is passed as the id Parameter.

Lastly, up near the top of the file the is some razor code for an Action link to create a new user. Replace the Action method parameter "Create" with our co-opted "Register" method.

After our modifications, the final Index.cshtml file should look like this:

Modified Index.cshtml File:
@model IEnumerable<AspNetRoleBasedSecurity.Models.EditUserViewModel>
@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
    @Html.ActionLink("Create New", "Register") 
</p>
<table class="table">
<tr>
<th>
            @Html.DisplayNameFor(model => model.UserName)
</th>
<th>
            @Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
            @Html.DisplayNameFor(model => model.LastName)
</th>
<th>
            @Html.DisplayNameFor(model => model.Email)
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
            @Html.DisplayFor(modelItem => item.UserName)
</td>
<td>
            @Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
            @Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
            @Html.DisplayFor(modelItem => item.Email)
</td>
<td>
            @Html.ActionLink("Edit", "Edit", new { id = item.UserName }) |
            @Html.ActionLink("Roles", "UserRoles", new { id = item.UserName }) |
            @Html.ActionLink("Delete", "Delete", new { id = item.UserName })
</td>
</tr>
}
</table>

 

Now we can get back to that whole User Roles issue.

Create the UserRoles.cshtml View

We can use the VS Add View method to create our UserRoles.cshtml View as we did with the previous views. However, we will be doing most of our work from scratch on this one. Right-Click on the UserRoles method of AccountController and select Add View. This time, however, choose the Empty template option from the Template drop-down, and choose SelectUserRolesViewModel from the Model Class drop-down.

You should now have a mostly empty UserRoles.cshtml file the looks like this:

The Empty UserRoles.cshtml File:
@model AspNetRoleBasedSecurity.Models.SelectUserRolesViewModel
@{
    ViewBag.Title = "UserRoles";
}
<h2>UserRoles</h2>

 

From here, we will add our code manually. We want to display the basic user information, followed by a list of the available roles to which the user can be assigned (or removed). We want the list of roles to feature checkboxes as the selection mechanism.

To accomplish the above, we will add our Razor code as follows:

Added Code to the UserRoles.cshtml File:
@model AspNetRoleBasedSecurity.Models.SelectUserRolesViewModel
@{
    ViewBag.Title = "User Roles";
}
<h2>Roles for user @Html.DisplayFor(model => model.UserName)</h2>
<hr />
@using (Html.BeginForm("UserRoles", "Account", FormMethod.Post, new { encType = "multipart/form-data", name = "myform" }))
{
    @Html.AntiForgeryToken()
<div class="form-horizontal">
        @Html.ValidationSummary(true)
<div class="form-group">
<div class="col-md-10">
                @Html.HiddenFor(model => model.UserName)
</div>
</div>
<h4>Select Role Assignments</h4>
<br />
<hr />
<table>
<tr>
<th>
                    Select
</th>
<th>
                    Role
</th>
</tr>
            @Html.EditorFor(model => model.Roles)
</table>
<br />
<hr />
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}

 

In the above, we have set up a display header featuring the UserName, and created an HTML Table with header elements for Select and Role. The Select column will contain the checkboxes for each role, and the Role column will, obviously, display the Role names.

Below the table header set up, notice the line:

@Html.EditorFor(model => model.Roles)

 

This is where we return to that EditorTemplate concept. An Editor template is a Shared View, and needs to be located in the Views => Shared => EditorTemplates folder in your project. You may need to create the folder yourself.

Create the SelectRoleEditorViewModel Editor Template by right-clicking on your new EditorTemplates folder and selecting Add View. Use the Empty template again, and name the view SelectRoleEditorViewModel (this is important). Choose SelectRoleEditorViewModel from the Model Classes drop-down. When you are done you should have a .cshtml file that looks like this:

The Empty SelectRoleEditorViewModel Editor Template File:
@model AspNetRoleBasedSecurity.Models.SelectRoleEditorViewModel
@{
    ViewBag.Title = "SelectRoleEditorViewModel";
}
<h2>SelectRoleEditorViewModel</h2>

 

From here, we will add a few lines, so that our file looks like this:

Modified SelectRoleEditorViewModel Editor Template File:
@model AspNetRoleBasedSecurity.Models.SelectRoleEditorViewModel
@Html.HiddenFor(model => model.RoleName)
<tr>
<td style="text-align:center">
        @Html.CheckBoxFor(model => model.Selected)
</td>
<td>
        @Html.DisplayFor(model => model.RoleName)
</td>
</tr>

 

Now we have an editor template for our SelectRoleEditorViewModel. The code in our UserRoles.cshtml View will use this template to render our list of roles, including the checkboxes. Selections made in the checkboxes will be reflected in our model and returned, with the Role name, to the controller when the form is submitted.

Adding the Admin Tab to the Main Site Page

We are almost there. However, none of these new views and functionality do us much good if we can't get to it from within our application. We need to add an Admin tab to our main site page, and also remove the ability for anonymous users to access the registration link included on the site by default.

To do this, we need to modify the _Layout.cshtml file in the Views => Shared folder.

Modified _Layout.cshtml File
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink("Home", "Index", "Home")</li>
<li>@Html.ActionLink("About", "About", "Home")</li>
<li>@Html.ActionLink("Contact", "Contact", "Home")</li>
<li>@Html.ActionLink("Admin", "Index", "Account")</li>
</ul>
    @Html.Partial("_LoginPartial")
</div>

 

The code above is from just about the middle of the _Layout.cshtml View file. Add the highlighted line to create a tab link pointing to the Index method of our AccountController.

Remove the Register Link from the _LoginPartial.cshtml View

Last, we want to remove the link to the Register method from the main site layout. This link is found on the _LoginPartial.cshtml file, again located in the Views => Shared folder. At the bottom of this file, remove the code line highlighted below:

Remove the Register Link from _LoginPartial.cshtml:
else
{
<ul class="nav navbar-nav navbar-right">
<li>@Html.ActionLink("Register", "Register", "Account", routeValues: null, htmlAttributes: new { id = "registerLink" })</li>
<li>@Html.ActionLink("Log in", "Login", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
</ul>
}

 

Now all our views should be ready.

Set Up and Run Entity Framework Migrations

Now that we have most of the pieces in place, it's time to set up Code-First Migrations with Entity Framework. Also, because we are ostensibly building an application in which only users in an administrative role can create or edit users, we need to seed our application with an initial Admin user. Further, since we don't plan to allow creation or modification of roles, we need to seed the database with the roles we expect to use in our application, since we can't create them from within the application itself.

We covered EF Code-First Migrations fairly thoroughly in the previous article, so I am going to skim through it this time.

First, enable migrations by typing the following in the Package Manager Console:

Enable EF Migrations in Your Project:
PM> Enable-Migrations –EnableAutomaticMigrations

 

Now, open the Migrations => Configuration.cs file and add the following code (tune it up to suit your specifics. You probably don't want to add MY information as your admin user. Also note, whatever password you provide to start with must conform to the constraints of the application, which appears to require at least one capital letter, and at least one number:

Modify the EF Migrations Configuration File with Seed Data:
using AspNetRoleBasedSecurity.Models;
using System.Data.Entity.Migrations;
namespace AspNetRoleBasedSecurity.Migrations
{
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
internalsealedclass Configuration : DbMigrationsConfiguration<ApplicationDbContext>
    {
public Configuration()
        {
            AutomaticMigrationsEnabled = true;
        }
protectedoverridevoid Seed(ApplicationDbContext context)
        {
this.AddUserAndRoles();
        }
bool AddUserAndRoles()
        {
bool success = false;
            var idManager = new IdentityManager();
            success = idManager.CreateRole("Admin");
if (!success == true) return success;
            success = idManager.CreateRole("CanEdit");
if (!success == true) return success;
            success = idManager.CreateRole("User");
if (!success) return success;
            var newUser = new ApplicationUser()
            {
                UserName = "jatten",
                FirstName = "John",
                LastName = "Atten",
                Email = "jatten@typecastexception.com"
            };
// Be careful here - you  will need to use a password which will 
// be valid under the password rules for the application, 
// or the process will abort:
            success = idManager.CreateUser(newUser, "Password1");
if (!success) return success;
            success = idManager.AddUserToRole(newUser.Id, "Admin");
if (!success) return success;
            success = idManager.AddUserToRole(newUser.Id, "CanEdit");
if (!success) return success;
            success = idManager.AddUserToRole(newUser.Id, "User");
if (!success) return success;
return success;
        }
    }
}

 

Once that is done, run the following command from the Package Manager Console:

Add the Initial EF Migration
Add-Migration Init

 

Then create the database by running the Update-Database command:

Update Database Command:
Update-Database

 

If all went well, your database should be created as a SQL Server (local) database in the App_Data folder. If you want to point to a different Database Server, review the previous article where we discuss pointing the default connection string to a different server.

You can check to see if your database was created properly by opening the Server Explorer window in Visual Studio. Or, of course, you could simply run your application, and see what happens!

Use [Authorize] Attribute to Control Access

From this point, we can regulate access to different application functionality using the [Authorize] Attribute. We have already seen examples of this on the methods in our AccountController, where access to everything except the Login method is restricted to users of the Admin role.

Use [Authorize] Attribute to Control Access to Functionality:
[AllowAnonymous]
public MyPublicMethod()
{
// Code
}
[Authorize(Role = "Admin", "CanEdit", "User")]
public MyPrettyAccessibleMethod()
{
// Code
}
[Authorize(Role = "Admin", "CanEdit")]
public MyMoreRestrictiveMethod()
{
// Code
}
[Authorize(Role = "Admin")]
public MyVeryRestrictedMethod()
{
// Code
}

 

In the code above, we see progressively more restricted method access based on Role access defined using the [Authorize] attribute.

At the moment, our role definitions are not "tiered" in a manner by which a higher-level role inherits the permissions associated with a more restricted role. For example, if a method is decorated with an [Authorize] attribute granting access to members of the User Role, it is important to note that ONLY members of that role will be able to access the method. Role access must be explicitly and specifically granted for each role under this scenario.

Role Permissions are not Inherited:
// Admins can't access this method:
[Authorize(Role = "Users")]
public SomeMethod()
{
// Code
}
// Admins AND Users can access this method:
[Authorize(Role = "Users", Admins)]
public SomeMethod()
{
// Code
}

 

Contrary to our experience with most operating system security, members of the Admin role do not automatically get all the permissions of the User role. We could probably achieve this, but such is beyond the scope of this article.

A Note About Roles and Role Naming

For our purposes here, I have used some rather generic role names, since we really don't have any business cases to consider when using roles to manage application access.

The ASP.NET team recommends (and I agree) the it is best to use descriptive and limiting role definitions which describe, to an extent, the permissions associated with that role. For example, instead of a generic "Admin" role, one might create an "IdentityManager" role specific to Account and Identity management, and other such descriptive role names as make sense in the business context of your application.

Wrapping It Up

In this article we have created a very simple implementation of Role-Based Identity Management. As I mentioned at the beginning, the model used here, outside of any business context, is a little basic. I have attempted to create show some of the basics involved with using the ASP.NET MVC Identity system, extending it to include some custom properties, and modifying the use to suit a basic business case.

There is a lot to know about Web Application security, and in my mind, it is not the place to re-invent any wheels. In the application discussed here, we have re-jiggered the components of the ASP.NET Identity model, but we have used the core pieces as designed instead of inventing our own authorization mechanism.

Hopefully, this rather long article has been helpful, and I have not propagated any bad information. Please do let me know, either in the comments, or by email if you find any significant issues here. I will correct them promptly.

Additional Resources and Items of Interest

C# - Generate and Deliver PDF Files On-Demand from a Template Using iTextSharp

$
0
0

antique-printing-press-500Often we need to provide a mechanism for users of our site to download content in the form of PDF files. While this in itself is not technically challenging, I recently had a need to generate customized PDF's on a per-user basis. The application in question basically provides a way for attendees of particular trainings to access their individualized certificates of completion through a link which is emailed to them.

It was decided that persisting the many individual certificates which might accumulate on the server over many trainings (we do a LOT of trainings each year) made no sense whatsoever.

Image by Curtis Ellis | Some Rights Reserved

Considering the application database already contains all the information required to generate an attendee-specific certificate from a template when one is needed, we decided that certificates would be created on a "just-in-time" basis, populated with the training and attendee specifics, and streamed to the user as a download, without ever being persisted to the server.

In this article, we will examine the specifics of this "Just-In-Time" PDF generation.

The Heart of the Matter – iTextSharp

As we discussed in a Splitting and Merging Pdf Files in C# Using iTextSharp, iTextSharp is a port of the Java-based iText library for working with and manipulating PDF files programmatically. We will need to pull iTextSharp into our project in order to do what we need to do. You can get iTextSharp binaries as described in the previous post, or you can take the easier approach, and use Nuget.

Get iTextSharp Using Nuget

Note that there are two versions of the iTextSharp library available via Nuget – the standard iTextSharp library, and the iTextSharp LGPL/MPL version. The licensing of the standard iTextSharp library is more restrictive, having moved to the GNU Affero General Public License (AGPL). In this project I used the LGPL/MPL version.

You can quickly install iTextSharp via the Nuget Package Manager Console by doing:

Install iTextSharp via Nuget Package Manager Console:
PM> Install-Package iTextSharp-LGPL

 

Basic Application Structure

For our purposes, we are intending to merge our data into a pre-existing PDF form template, and then flattening the PDF document, including the populated form fields. We will do this entirely in memory, and then stream the result back to the caller.

We will first set up a core class, PdfMergeStreamer, which will take in a file path (which points to the PDF Form template), an IEnumerable<IPdfMergeData> , and a System.IO.MemoryStream. IPdfMergeData is an interface which will represent an object containing a Dictionary mapping text values (the Dictionary values) to the Pdf Form fields in our template by name (the Dictionary keys).

Our PdfMergeStreamer class will basically consume a list of one or more merge data items, populate and flatten the template for each, and add each to a single output document (think Word Merge, but with PDF). Of course, there's more to it than that, and we'll walk through it in a moment.

The basic idea was to create a generalized class which can be used to populate any PDF form template, so long as it is provided with the correct values for each field in the template.

The PdfMergeStreamer Class

PdfMergeStreamer Class:
using iTextSharp.text;
using iTextSharp.text.pdf;
using System.Collections.Generic;
using TrainingDepartmentAzure.Models;
namespace TrainingDepartmentAzure
{
publicclass PdfMergeStreamer
    {      
publicvoid fillPDF(string templatePath, IEnumerable<IPdfMergeData> mergeDataItems,
            System.IO.MemoryStream outputStream)
        {
// Agggregate successive pages here:
            var pagesAll = new List<byte[]>();
// Hold individual pages Here:
byte[] pageBytes = null;
foreach (var mergeItem in mergeDataItems)
            {
// Read the form template for each item to be output:
                var templateReader = new iTextSharp.text.pdf.PdfReader(templatePath);
using (var tempStream = new System.IO.MemoryStream())
                {
                    PdfStamper stamper = new PdfStamper(templateReader, tempStream);
                    stamper.FormFlattening = true;
                    AcroFields fields = stamper.AcroFields;
                    stamper.Writer.CloseStream = false;
// Grab a reference to the Dictionary in the current merge item:
                    var fieldVals = mergeItem.MergeFieldValues;
// Walk the Dictionary keys, fnid teh matching AcroField, 
// and set the value:
foreach (string name in fieldVals.Keys)
                    {
                        fields.SetField(name, fieldVals[name]);
                    }
// If we had not set the CloseStream property to false, 
// this line would also kill our memory stream:
                    stamper.Close();
// Reset the stream position to the beginning before reading:
                    tempStream.Position = 0;
// Grab the byte array from the temp stream . . .
                    pageBytes = tempStream.ToArray();
// And add it to our array of all the pages:
                    pagesAll.Add(pageBytes);
                }
            }
// Create a document container to assemble our pieces in:
            Document mainDocument = new Document(PageSize.A4);
// Copy the contents of our document to our output stream:
            var pdfCopier = new PdfSmartCopy(mainDocument, outputStream);
// Once again, don't close the stream when we close the document:
            pdfCopier.CloseStream = false;
            mainDocument.Open();
foreach (var pageByteArray in pagesAll)
            {
// Copy each page into the document:
                mainDocument.NewPage();
                pdfCopier.AddPage(pdfCopier.GetImportedPage(new PdfReader(pageByteArray), 1));
            }
            pdfCopier.Close();
// Set stream position to the beginning before returning:
            outputStream.Position = 0;
        }
    }
}

 

As we can see, this result in a rather monolithic chunk of code, and could probably be refactored. For now, though, we will leave it as is.

Now let's take a look at what we are passing in. The templatePath is, obviously, a path to a file on the local file system. outputStream is passed in by the caller, and will be used once this method returns to consume or otherwise use the resulting PDF file (in my case, streaming it to the end user as a file download). Which leaves our IEnumerable<IPdfMergeData>.

In my case I decided I wanted to be able to create different merge templates, which required different sets of merge fields, all of which would need to be mapped according to the specific data required for the merge. While we could have simply passed in a Dictionary<string, string> straight way, I decided that creating a specific interface would make the intent more clear, and also force me to write a concrete implementation for each mapping.

The interface itself is straightforward:

The IPdfMergeData Interface:
publicinterface IPdfMergeData
{
    IDictionary<string, string> MergeFieldValues { get; }
}

 

Creating a PDF Form Template

There are a number of options for creating a PDF Form template, the first of which that comes to mind being (obviously) Adobe Acrobat. However, Acrobat is a little spendy, so if you don't have access to that, you might avail yourself of the Open-Source program, Scribus.

Scribus is not a PDF creation program per se, in fact it is a page layout application. However, it present an Export to PDF option, and allows us to place named form fields on our page, along with text, images, and other content.

There is a small learning curve to using Scribus to produce a workable PDF form, and I will discuss that in another post. However, three suggestions to consider when using Scribus to make your form:

  • Place form fields on their own layer in the Scribus Document. This keeps the fields separated from other content, and makes editing much easier.
  • When you export, use use the PDF 1.4 (Acrobat 5) setting. There seem to be issues with the other PDF settings.
  • Be careful with font choices when using Scribus. While most fonts will appear correctly in the Scribus document itself, when exporting to PDF they do not always work.
  • Make sure to name your fields carefully when adding them to the PDF Form. The field names must match the keys you provide in your concrete implementation of IPdfMergeData

Obviously, using Acrobat is preferred for creating a template for our purpose. However, I had to use Scribus to create a template for my own application, and after a little trial and error, it worked just fine.

A Real-World Example

Ok, now that we have the basic pieces in place, let's walk through a simplified example of how I implemented the rest of this. This will help you see how you might create your own implementation to meet your own domain-specific requirements.

In my ASP.NET MVC application, one set of my (simplified) requirements are:

  • Training attendees should be able to download a personalized certificate via a link received by email
  • System Users should be able to create and download a batch of certificates for all attendees for a particular training (sometimes the training sponsor wants the whole thing printed and mailed to them)
  • System Users should be able to download individual attendee certificates (sometimes attendees don't have email, or need us to send a certificate directly to a third party. Or, have trouble downloading from the link and need the certificate sent as an actual attachment.
  • Certificate PDF files should not be persisted on the server, but instead will be streamed directly to the client browser after generation.

We won't cover all the details of building out this application here – we will focus on the implementation of the certificate generation on demand. Let's start with the methods needed on AttendeeCertificatesController.

The Attendee Certificates Controller – Where It All Begins

Among the other methods on our controller, two are of specific interest to us here, DownloadCertificate and DownloadBatch. These will a return File object in the response body representing an individual certificate, or a batch or certificates respectively, which will be downloaded by the client's browser.

What is important to note here is that all of the processing happens in memory, and the resulting certificate is not persisted locally on the server.

Note that for simplicity, I have not implemented much exception handling here, or async processing (which might be in order, since processing a batch of a few hundred certificates could take some time). We'll look at DownloadCertificate first.

The DownloadCertificate Method of AttendeeCertificatesController
[AllowAnonymous]
public ActionResult DownloadCertificate(int trainingId, string attendeeGuid = null)
{
string validationMessage = "";
    Guid guid;
try
    {
if (Guid.TryParse(attendeeGuid, out guid))
        {
            var Db = new TrainingDbAzureEntities();
            var table = Db.Attendees;
            var attendee = table.First(a => a.AttendeeGuid == guid);
            trainingId = attendee.TrainingId;
string templatePath = @AppDomain.CurrentDomain.BaseDirectory 
            + @"PdfTemplates\CertificateTemplateForPdf.pdf";
            var streamer = new AttendeeCertificateStreamer();
            var pdfMemoryStream = streamer.GetPdfStream(attendee, templatePath);
string contentType = "application/pdf";
            var cd = new System.Net.Mime.ContentDisposition();
            cd.Inline = false;
            cd.FileName = this.getPdfFileName(attendee.FullName, attendee.AttendeeGuid);
            Response.AppendHeader("Content-Disposition", cd.ToString());
return File(pdfMemoryStream.ToArray(), contentType);
        }
else
        {
            validationMessage = "The certificate you have requested does not exist.";
return Index(trainingId, validationMessage);
        }
    }
catch (Exception)
    {
returnnull;
    }
}

 

In the above, notice we have defined a helper method, getPdfFileName which takes some attendee-specific input and creates a suitable name for the file prior to download. The idea was to create a file name with a user-friendly component, but which would be suitably unique. The method is used by both DownloadCertificate and DownloadBatch, and is as follows:

The getPdfFileName Method:
string getPdfFileName(string friendlyName, Guid guid)
{
    var rgx = new System.Text.RegularExpressions.Regex("[^a-zA-Z0-9 -]");
string result = rgx.Replace(friendlyName, "");
returnstring.Format("{0}-{1}.pdf", friendlyName, guid.ToString());
}

 

We route requests to DownloadCertificate by placing the following custom route definition in our RouteConfig.RegisterRoutes method:

Route Definition for DownloadCertificate:
routes.MapRoute(
    name: "AttendeeCertificateDownload",
    url: "AttendeeCertificates/Download/{trainingId}/{attendeeGuid}",
    defaults: new { controller = "AttendeeCertificates", action = "DownloadCertificate" }
);

 

When a request is routed to the DownloadCertificate method above, we can see that the attendeeGuid route parameter is used to look up the specific attendee record in the database (the Guid in the database is not used as the primary key, hence the slightly more clumsy lookup).

Next, we grab the path to our PDF form template, and then initialize an instance of a new class, AttendeeCertificateStreamer. What, you say? We haven't discussed this one yet.

The Attendee Certificate Streamer Class

TheAttendeeCertificateStreamer provides a layer of abstraction between our controller and the PdfMergeStreamer class we examined previously, and handles the domain-specific implementation details related to creating certificates of attendance before calling into the more general PdfMergeStreamer.

As we can see in the following code, AttendeeCertificateStreamer accepts attendee data from our controller and maps it into a form useable by the first class we examined, PdfMergeStreamer.

The AttendeeCertificateStreamer Class:
publicclass AttendeeCertificateStreamer
{
public System.IO.MemoryStream GetPdfStream(IEnumerable<Attendee> attendees, 
string templatePath)
    {
        var util = new PdfMergeStreamer();
        var pdfMemoryStream = new System.IO.MemoryStream();
        IPdfMergeData mergeData = this.getAttendeeMergeData(attendees);
        util.fillPDF(templatePath, mergeData, pdfMemoryStream);
return pdfMemoryStream;
    }
public System.IO.MemoryStream GetPdfStream(Attendee attendee, 
string templatePath)
    {
        var attendees = new List<Attendee>();
        attendees.Add(attendee);
returnthis.GetPdfStream(attendees, templatePath);
    }
    IEnumerable<IPdfMergeData> getAttendeeMergeData(IEnumerable<Attendee> attendees)
    {
        var output = new List<IPdfMergeData>();
foreach (var attendee in attendees)
        {
            output.Add(new AttendeeCertificateMergeData(attendee));
        }
return output;
    }
}

 

In the above, we pass our template path and a single instance of Attendee to the second of the two GetPdfStream methods. The single instance is added to a list and passed to getAttendeeMergeData, which performs the mapping we need for our template. As we can see, the getAttendeeMergeData method uses each instance of Attendee to initialize a new instance of AttendeeCertificateMergeData.

Implementing IPdfMergeData: Template-Specific Mapping

Remember our interface, IPdfMergeData? AttendeeCertificateMergeData is the concrete implementation we will use specifically for mapping to our PDF Form template.

Template-Specific Implementation of IPdfMergeData:
publicclass AttendeeCertificateMergeData : IPdfMergeData
{
    Attendee _attendee;
public AttendeeCertificateMergeData(Attendee attendee)
    {
        _attendee = attendee;
    }
public IDictionary<string, string> MergeFieldValues
    {
get { returnthis.getMergeDictionary(); }
    }
    IDictionary<string, string> getMergeDictionary()
    {
        var output = new Dictionary<string, string>();
        var training = _attendee.Training;
        output.Add("FullName", _attendee.FullName);
        output.Add("CourseTitle", training.CourseTitle);
        var dyl = string.Format("{0}, {1} in {2}", 
            training.TrainingPeriod, training.Year, training.Location);
        output.Add("DatesYearLocation", dyl);
        output.Add("EndDate", training.EndDate.ToShortDateString());
        output.Add("CEHours", training.CEHours.ToString());
return output;
    }
}

 

As we can see in the above, I am able to simply pass an instance of Attendee in to the constructor, and I am ready to go. The private method getMergeDictionary is called when the MergeFieldValues property is accessed, and returns a Dictionary containing key-value pairs for each of my template fields.

Clearly, there is not a one-to-one mapping between properties of Attendee and fields in my template form. In some cases, the form template requires concatenations, and/or other manipulations of attendee data to make it suitable for presentation.

Obviously, each dictionary key must be carefully mapped by name to the corresponding Form field in the PDF Form template.

Application Flow

If we look through the code for AttendeeCertificateController, AttendeeCertificateSreamer, and PdfMergeStreamer, we find we have a basic flow that looks like the diagram below.

Simplified Application Flow:

 

Flow Diagram

An HTTP request is routed to our controller, where the incoming route parameters are used to retrieve an instance of a specific attendee. From there, the attendee is passed, along with a path pointing to a locally persisted PDF form template file, to an instance of AttendeeCertificateStreamer.

AttendeeCertificateStreamer accepts the attendee instance and creates an instance of AttendeeCertificateMergeData, our concrete implementation of IPdfMergeData. This, along with the template path and a newly-created System.IO.MemoryStream are then passed to PdfMergeStreamer.

PdfMergeStreamer processes the data, and adds the merged PDF file to the MemoryStream, which is returned by AttendeeCertificateStreamer back to our controller. The controller sets the content disposition, file name, and content type and returns a FileContentResult, which adds our file stream to the HTTP response body, and is ultimately downloaded by the client's browser.

The Download Batch Method of Attendee Certificates Controller

It was recognized that in addition to attendees being able to download certificates individually, it would also be necessary for system users to be able to create and download whole batches of certificates for all attendees. Sometimes the training sponsor wants these printed and mailed.

In reality, the core class, PdfMergeStreamer is set up to handle batches from jump – in fact that is the only way it works. We have thus far examined what is actually the special case – a single certificate (in other words, a "batch" of one).

Nonetheless, making a single certificate available to a specific attendee through a link is a different scenario than allowing internal system users to access certificate data in bulk, and hence requires a slightly modified controller method.

The Download Batch Method on Attendee Certificates Controller:
[Authorize]
public ActionResult DownloadBatch(Training training, IEnumerable<Attendee> attendees)
{
string validationMessage = "";
try
    {
if (attendees.Count() > 0)
        {
string templatePath = @AppDomain.CurrentDomain.BaseDirectory 
                + @"PdfTemplates\CertificateTemplateForPdf.pdf";
            var streamer = new AttendeeCertificateStreamer();
            var pdfMemoryStream = streamer.GetPdfStream(attendees, templatePath);
string contentType = "application/pdf";
            var cd = new System.Net.Mime.ContentDisposition();
            cd.Inline = false;
            cd.FileName = this.getPdfFileName(training.FullName, training.TrainingGuid);
            Response.AppendHeader("Content-Disposition", cd.ToString());
return File(pdfMemoryStream.ToArray(), contentType);
        }
else
        {
            validationMessage = "You must select at least one attendee.";
return Index(training.TrainingId, validationMessage);
        }
    }
catch (Exception)
    {
throw;
    }
}

 

As we can see above, there is not much different between this and the previously discussed DownloadCertificate method. Primarily, this method accepts an IEnumerable<Attendee> as an argument, and is flagged with an [Authorize] attribute, meaning only authorized system users may access this method.

Requests are routed to the DownloadBatch method by adding the following route definition to the Route.config file:

routes.MapRoute(
    name: "DownloadBatch",
    url: "AttendeeCertificates/DownloadBatch",
    defaults: new { controller = "AttendeeCertificates", action = "DownloadBatch" }
);

 

Wrapping Up

Obviously, the examples in this article are tuned to the specific needs of my own application (though a simplified things as much as possible). However, the first class we examined, PdfMergeStreamer, in conjunction with the interface IPdfMerge data, should get you started. Feel free to email me or comment with questions, or to point out errors. Feedback is welcome and appreciated.

Additional Resources and Items of Interest

ASP.NET MVC: Show Busy Indicator on Form Submit using JQuery and Ajax

$
0
0

ajaxAs we all know, users are impatient. We also know that if we don't tell them our application is "doing something" they tend to do silly things like click the mouse repeatedly, seeking some sign that the requested action is indeed being performed.

For this reason, it is a good idea to throw up some sort of "busy" indicator when a user-initiated action may invoke a long-running process which requires them to wait.

In this article we are going to demonstrate a very basic way to achieve this which will be effective in most cases.

If you are new to ASP.NET MVC, you may want to check out my previous posts on Routing Basics and Route Customization as well. While not critical to understanding what we discuss here, some background on the basics of MVC routing will certainly help in understanding what's going on in places.

Image by Martin Abegglen | Some Rights Reserved

The fully-functioning source code for the example used in this article is available from my Github Repo. You will need to use Nuget Package Restore to pull down all the package goodness so that the project will run. If you are unsure what that means, see Keep Nuget Packages Out of Source Control with Nuget Package Manager Restore.

What the Hell is Ajax?

Ajax is an acronym for Asynchronous JavaScript and XML. Ajax represents a broad range of technologies used to facilitate client-server communication without interfering with the current state of the page.

In the main, we most often use the term when we speak of making an Ajax Request to the server, usually in the context of posting or retrieving data, and updating only a portion of the page, as opposed to completely refreshing a page simply because a small sub-area needs to be updated.

Upon the introduction of the term circa 2005, XML represented what many believed would be the primary data interchange format for this type of client-server transaction. Since then, JavaScript Object Notation (JSON) has become more and more of a standard. While XML is not gone, you are as likely to send and receive JSON in the course of an Ajax request as you are XML.

At present, we often find Ajax used in conjunction with JQuery (more properly, one often uses JQuery to make an Ajax request) when we need to retain the current state of our page while a request is made to the server and then update the portion of the page affected by the information returned from the request.

Adding a Busy indicator to a Page with JQuery and Ajax

The common way to display a busy indicator on a page while we wait for a long-running request to return from the server is to throw up an animated Gif, to let users know something is in fact happening. Equally as often, we need to then remove or hide the animation when the process completes, and refresh the portion of the page affected by our new data.

There really is no good way to do this from the server side in an MVC application – we need to use Ajax. We do this with JavaScript. in this case, we are going to use the popular JQuery library, because it is ubiquitous, it ships with and in integrated into the core MVC project.

Let's take a look at a quick example.

Basic Example – The View

First we will look at a basic view. We've kept it simple here – we will display a couple of data entry fields, and a button to submit the data entered by the user. The essential cshtml code for this is as follows:

A Basic View:
@model JqueryBusyExample.Models.DemoViewModel
@{
    ViewBag.Title = "Home Page";
}
<h3>Enter Your Name and Submit:</h3>
@using (Html.BeginForm("LongRunningDemoProcess", 
"Home", FormMethod.Post, 
new { encType = "multipart/form-data", id="myform", name = "myform" }))
{
<div class="form-group">
        @Html.LabelFor(model => model.FirstName, 
new { @class = "control-label col-md-2" })
<div class="col-md-10">
            @Html.EditorFor(model => model.FirstName)
            @Html.ValidationMessageFor(model => model.FirstName)
</div>
</div>
<div class="form-group">
        @Html.LabelFor(model => model.LastName, 
new { @class = "control-label col-md-2" })
<div class="col-md-10">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
</div>
</div>
<input type="submit" name="operation" id="process" value="process" />
}

 

As we can see, there is nothing unusual here. We use standard Razor syntax to create a form with a couple data entry fields for First and Last names, and a submit button. In the opening of the using statement, The form is wired to a method named LongRunningDemoProcess on our HomeController.

In the form declaration, the syntax is:

Html.BeginForm(<actionName>, <controllerName>, <Http Method Type>, <Html Attributes>)

 

The above basically determines what happens when the form is submitted, and specifies a specific method, and a specific controller as a target for the Http request. It also defines the request method type (GET, POST, PUT, DELETE).

In our example case, we want to send our model data in the POST body. On our controller, the LongRunningDemoProcess method will need to be decorated with the [HttpPost] attribute, and accept the POST body payload as an argument.

Now let's take a look at our Home controller.

Basic Example – The Controller

For our purpose here, I have simply added a method named LongRunningDemoProcess to the stock ASP.NET MVC Home Controller which is part of the default MVC 5 project template:

A Basic Controller with Long-Running Process:
publicclass HomeController : Controller
{
public ActionResult Index()
    {
return View();
    }
    [HttpPost]
public JsonResult LongRunningDemoProcess(DemoViewModel model)
    {
        Thread.Sleep(1000);
return Json(model, "json");
    }
public ActionResult About()
    {
        ViewBag.Message = "Your application description page.";
return View();
    }
public ActionResult Contact()
    {
        ViewBag.Message = "Your contact page.";
return View();
    }
}

 

In the above, we use Thread.Sleep to mimic a long-running process on the server. You will need to add a reference to the System.Threading namespace in the using statement imports at the top of your file.

As we can see, LongRunningDemoProcess is the target of for our HTTP Post request from the Index view when form submission occurs. We want to use the Json data returned from the request to update our view, and let the user know their submit succeeded.

At the moment, though, things are not all they could be. We can load up our page, type some data into the form, and hit submit. What happens next, though, is that our page freezes up while the long-running process runs, and then our Json data is returned to a new tab in our browser (Chrome) or we are prompted to Save or Open (IE). 

What we WANT to happen is to display a busy indicator, and then somehow indicate to the user that their submission was successful (or something!).

Get a Busy Indicator GIF

One of the more useful little sites I've found recently is AjaxLoad.Info, which presents a handy library of various customizable animated GIFs. You can specify some basic parameters (Size, Type, Foreground Color, etc.) and then download, ready to use.

Go get one, add it to your project in a location which makes sense (I am using ASP.NET MVC 5 project in VS 2013, so I placed mine at the root level of the Content folder).

Next, let's modify our view, and add a div we can show and hide as needed, and which contains our gif:

The View, with Animated GIF and Placeholder for Submission Result

In the highlighted area below, we have added a div element to contain our Gif. While we're at it, we also added another div, to hold the result when our long-running process returns:

Modified View with Animated Gif and Placeholder Div:
@model JqueryBusyExample.Models.DemoViewModel
@{
    ViewBag.Title = "Home Page";
}
<h3>Enter Your Name and Submit:</h3>
@using (Html.BeginForm("LongRunningDemoProcess", "Home", FormMethod.Post, 
new { encType = "multipart/form-data", id="myform", name = "myform" }))
{
<div class="form-group">
        @Html.LabelFor(model => model.FirstName, 
new { @class = "control-label col-md-2" })
<div class="col-md-10">
            @Html.EditorFor(model => model.FirstName)
            @Html.ValidationMessageFor(model => model.FirstName)
</div>
</div>
<div class="form-group">
        @Html.LabelFor(model => model.LastName, 
new { @class = "control-label col-md-2" })
<div class="col-md-10">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
</div>
</div>
<input type="submit" name="operation" id="process" value="process" />
}
// We want to show this while the server process is running:
<div id="divProcessing">
<p>Processing, please wait . . . <img src="../../Content/ajax-loader.gif"></p>
</div>
// We want to display the result from our submission in here:
<div id="divResult">
</div>

 

Now, in order to tie all this together, we need to add an Ajax request.

Adding the Ajax Request

The easiest way to achieve what we want is to use JavaScript on the client to show our animated gif, and then submit our data to the server. We also want to be able to respond when the Json is returned by the server by updating our page, without refreshing the whole thing.

For our example, I am going to add the JavaScript right on the view. This may or may not make sense in a production application, but we will do it here for simplicity.

I've added the JQuery code below our now-familiar view:

The View, with JQuery Added:
@model JqueryBusyExample.Models.DemoViewModel
@{
    ViewBag.Title = "Home Page";
}
<h3>Enter Your Name and Submit:</h3>
@using (Html.BeginForm("LongRunningDemoProcess", "Home", FormMethod.Post, 
new { encType = "multipart/form-data", id="myform", name = "myform" }))
{
<div class="form-group">
        @Html.LabelFor(model => model.FirstName, 
new { @class = "control-label col-md-2" })
<div class="col-md-10">
            @Html.EditorFor(model => model.FirstName)
            @Html.ValidationMessageFor(model => model.FirstName)
</div>
</div>
<div class="form-group">
        @Html.LabelFor(model => model.LastName, 
new { @class = "control-label col-md-2" })
<div class="col-md-10">
            @Html.EditorFor(model => model.LastName)
            @Html.ValidationMessageFor(model => model.LastName)
</div>
</div>
<input type="submit" name="operation" id="process" value="process" />
}
// We want to show this while the server process is running:
<div id="divProcessing">
<p>Processing, please wait . . . <img src="../../Content/ajax-loader.gif"></p>
</div>
// We want to display the result from our submission in here:
<div id="divResult">
</div>
@section Scripts {
  @Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
  $(document).ready(function () {
    $(document).ready(function () {
// Hide the "busy" Gif at load:
      $("#divProcessing").hide();
// Attach click handler to the submit button:
      $('#process').click(function () {
          $('#myform').submit();
      });
// Handle the form submit event, and make the Ajax request:
      $("#myform").on("submit", function (event) {
event.preventDefault();
// Show the "busy" Gif:
        $("#divProcessing").show();
        var url = $(this).attr("action");
        var formData = $(this).serialize();
        $.ajax({
          url: url,
          type: "POST",
          data: formData,
          dataType: "json",
          success: function (resp) {
// Hide the "busy" gif:
            $("#divProcessing").hide();
// Do something useful with the data:
            $("<h3>" + resp.FirstName + "" + resp.LastName + "</h3>").appendTo("#divResult");
          }
        })
      });
    });
  });
</script>
}

 

In the above, we start with the standard JQuery document.ready function, and hide the div containing our animated Gif.

We then attach the form submit event to the click event of the submit button we defined in our View (Isn't it already attached, you ask? We'll get to why we do this in a moment), and then we handle the form submit event.

Handling the Form Submit Event with an Ajax Request

This is where things get interesting. There are a few things occurring inside this function, in a rather specific order. Let's walk through it. 

First, we want to prevent the default action using (wait for it . . .) the JQuery preventDefault() method when the form submit event occurs, otherwise, form submission will proceed automatically, as before, and any code we place in our handler will not work properly.

Next, we show our animated Gif. Once shown, it will do its thing, giving the user the impression that something  useful is happening.

Finally, we collect what we need from our form in order to submit our Ajax request. First, we grab the Url and stow it in a variable by accessing the form's action attribute using JQuery. The action attribute essentially pulls a Url which matches the route we specified in the form declaration (remember?  ActionMethod, ControllerName, HttpMethodType, etc?).

Next, we serialize the form data ( in this case, our model data) into another variable, again using JQuery.

Once we have collected these things, we can set up our Ajax request by making the property assignments shown. Note that we need to specify the data type as Json, so that when the request returns, JQuery will recognize it as valid Json. Then, we come to the all-important success property.

We have assigned a function here, which will execute when our long-running process returns a response. The resp argument will contain the Json returned from our controller method. In this very simple case, it is merely the data we already submitted. However, it could just as easily be the result of persisting some data in a database on the server. In any case, we know that when this function is called, our remote, long-running task has completed.

That being the case, the first thing we want to do is hide our animated Gif. Next, we push the returned data into the div we set up as a placeholder (after doing a little formatting, of course).

Fairly Simple, but Not Always Obvious

And there you have it. The example we have examined is pretty basic, but gives a good look at how to both display a busy indicator, and how to make Ajax requests from your ASP.NET MVC page.

There is a lot more to learn about Ajax, and it provides a foundation for much of the interactivity found in today's modern websites. I welcome comments and constructive criticism in the comments section below (especially if I got something wrong here . . .)!

You can clone or download the example project from my Github Repo. Note that you will need to use Nuget Package Restore to pull in all the package dependencies. If you don't know what that means, see Keep Nuget Packages Out of Source Control with Nuget Package Manager Restore.

Additional Resources and Items of Interest

ASP.NET MVC: Add a Select All Checkbox to a Checklist Table Using JQuery

$
0
0

jQuery-CSS-LogoOften, we find we need to present our users with a list of items in a table or list, with checkboxes such that the user can select one or more items from the list for additional processing. Most of the time, the user will either be selecting a few items from the list but leaving most unchecked, or selecting all or most of the items from the list and leaving very few items unchecked.

In these cases, it is handy to offer the user a means to "Select All" or "Select None" which then toggles the checked state of the entire list. The common means to do this is with one more checkbox, global to the list itself, which performs the toggling action.

One way to do this is to use JQuery.

The Example Project

We will use an existing example project I created to demonstrate how to display a table with checkboxes for each row, which allows submission to the server for processing the selected items. Here, we will focus on the selection toggling portion of the Index View.

You can find the source code for the example project on my Github repo.

If you clone or download the source from Github, you will need to Enable Nuget Package Restore in order that VS can automatically download and update Nuget packages when the project builds.

The Index View, with Selectable Items in a Table

For our purposes, we will only need to look at the Index view, which presents a table where each row features a checkbox on the leftmost side allowing the user to select one or more individual row items from the list.

The original code for the View looks like this:

Index View with a Table and Checkboxes:
@model AspNetCheckedListExample.Models.PeopleSelectionViewModel
@{
    ViewBag.Title = "People";
}
<h2>People</h2>
@using (Html.BeginForm("SubmitSelected", "People", FormMethod.Post, new { encType = "multipart/form-data", name = "myform" }))
{
<table class="table">
<tr>
<th>
                Select
</th>
<th>
                Name
</th>
<th></th>
</tr>
        @Html.EditorFor(model => model.People)
</table>
<hr />
<br />
<input type="submit" name="operation" id="submit" value="Submit Selected" />
}

 

In the above, we set up a table, and use a custom Editor Template to render each row item, allowing for the selectable textbox on each row, the status of which will be reflected in the model data passed back to the controller in an Http POST request on form submit.

We want to add a checkbox above the table, selection of which toggles the checked status of all the checkboxes in the table at once.

Add the Select All Checkbox

To do this, we will modify the form on our Index page by adding a <div> element containing our global checkbox, which we will name "checkall", and then also wrap our Html table in a div named "checkboxes" that we can then manipulate using JQuery:

Add Div Elements to HTML Form on the Index Page:
@using (Html.BeginForm("SubmitSelected", "People", FormMethod.Post, new { encType = "multipart/form-data", name = "myform" }))
{
// Add a new checkbox in its own div element:
<div>
<input type="checkbox" id="checkall" /><span>Check All</span>
</div>
// Wrap the table element in a div named "checkboxes":
<div id="checkboxes">
<table class="table">
<tr>
<th>
                    Select
</th>
<th>
                    Name
</th>
<th></th>
</tr>
            @Html.EditorFor(model => model.People)
</table>
    @*Close the "checkboxes" div element:*@
</div>
<hr />
<br />
<input type="submit" name="operation" id="submit" value="Submit Selected" />
}

 

JQuery Code to Manipulate the Checkboxes

Now we will add some JQuery to perform the manipulation when we want to toggle the checkbox status. For simplicity, we are going to add this code right on our page. Note that there are myriad ways to do this, and you may see some methods out on the internet which are slightly different.

Under our view code, we will add a <scripts> section, and add the following JQuery:

Add JQuery to Toggle the Checkbox Status for the Table Row Items:
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
        function toggleChecked(status) {
            $("#checkboxes input").each(function () {
// Set the checked status of each to match the 
// checked status of the check all checkbox:
                $(this).prop("checked", status);
            });
        }
        $(document).ready(function () {
//Set the default value of the global checkbox to true:
            $("#checkall").prop('checked', true);
// Attach the call to toggleChecked to the
// click event of the global checkbox:
            $("#checkall").click(function () {
                var status = $("#checkall").prop('checked');
                toggleChecked(status);
            });
        });
</script>

 

In the above code, We define a function, toggleChecked() , which sets the checked status of each checkbox in the table to match that of the global "checkall" checkbox. Then, in the JQuery (document).ready() function, we the default value of the global "checkall" checkbox to true, and attach a call to the toggleChecked() function to the click event of the global "checkall" checkbox.

As we can see, it is not a complex matter to do this, but it may not be intuitive to those newer to Jquery.

As I mentioned, there are myriad ways to do this out there on the web. if you have seen a better way, or noticed something I have done above that it just plain stupid, please do let me know in the comments, or drop me an email at the address in the About the Author section.

Additional Resources and Items of Interest

ASP.NET MVC Display an HTML Table with Checkboxes to Select Row Items

$
0
0

6823243264_31ec905ee4Often we need to create a table for our ASP.NET MVC web application with a checkbox next to each row, in order to allow the user to select one or more items from our list for additional processing. While the concept is not terribly challenging, it is not intuitive or obvious if you are newer to MVC.

Here is one way to do it.

Image by Elif Ayiter | Some Rights Reserved

The Example Project

For our example we will simply display a list of people as rows in a table, with checkboxes to the leftmost end of each row. The user may select one or more people from the list, click a submit button, and implement some sort of processing for the individual items selected.

Our example assumes that the business model requires server-side processing of the selected items, and therefore a direct server request instead of AJAX-style processing.

You can find the working source code at my Github repo.

If you clone or download the source from Github, you will need to Enable Nuget Package Restore in order that VS can automatically download and update Nuget packages when the project builds.

Editor View Models and Editor Templates

ASP.NET MVC affords us the ability to create custom editor templates for our domain classes and models. Similar to the convention-based relationship between Controllers and Views, there is a similar convention-based relationship between Editor View Models and Editor Templates.

The MVC framework already defines its own library of Editor Templates, made available behind the scenes through methods such as EditorFor(), DisplayFor(), LabelFor(), etc. For example, when you place code like this in your view:

<div class="col-md-10">
    @Html.EditorFor(model => model.LastName)
    @Html.ValidationMessageFor(model => model.LastName)
</div>

 

MVC understands it is to retrieve the proper editor template from System.Web.Mvc.dll for a string to contain the model.LastName property on our page. The default display device will be a textbox.

Utilizing this convention, we can create a custom Editor View Template for Models or View Models within our domain which can then be used in our code. Employing an editable checkbox for each row in a table, the status of which can be sent to and from the server as a member in a list, is one case where we need to employ an Editor View Model and an Editor View Template to make things work.

How we do this becomes more clear as we step through the example code.

The Core Domain Model

For our example, we will start with a simple domain model, represented by the Person class:

The Person Model:
publicclass Person
{
publicint Id { get; set; }
public string LastName { get; set; }
public string firstName { get; set; }
}

 

Add Test Data

In order to keep our example simple, we will create a test data repository for our code to work with, so we can pretend we are working with a database. We will add the following to our Models folder:

Test Data for the Example Code:
publicclass TestData
{
public TestData()
    {
this.People = new List<Person>();
this.People.Add(new Person() { 
            Id = 1, LastName = "Lennon", firstName = "John" });
this.People.Add(new Person() { 
            Id = 2, LastName = "McCartney", firstName = "Paul" });
this.People.Add(new Person() { 
            Id = 3, LastName = "Harrison", firstName = "George" });
this.People.Add(new Person() { 
            Id = 4, LastName = "Starr", firstName = "Ringo" });
    }
public List<Person> People { get; set; }
}

 

The above will allow us to act as though we have a backend data store.

Adding the Editor View Model

Now let's create an Editor View Model which will allow us to present each Person instance as a row in a table, and include a Selected status that we can present as a checkbox for each table row.

There are two important things to note about Editor View Models and their relationship to the Editor Template for which they are designed:

The Editor View Model class name must follow the convention ClassNameEditorViewModel

The Corresponding Editor Template must be located in a folder named EditorTemplates, within either the Views => MyClass folder, or the Views => Shared folder. We'll see more on this in a moment. For now, be aware that the naming of the View Model and the associated View is critical, as is the location of the .cshtml View file itself.

Add the following class to the Models folder. In keeping with the above, we have named our View Model SelectPersonEditorViewModel:

The Select Person Editor View Model
publicclass SelectPersonEditorViewModel
{
public bool Selected { get; set; }
publicint Id { get; set; }
public string Name { get; set; }
}

 

Looking over the code above, we see that we have included only the information required by our view, and in fact have provided only a single string property for Name. We could have included separate FirstName and LastName properties, but I decided for our list we would just display the concatenated First and Last names for each Person.

This Editor View Model will be used by MVC to render each row in our table. Now, we need to create a wrapper for the collection of rows data.

Adding the Wrapper View Model

We need a wrapper to contain a list of the EditorViewModels we wish to pass to and from our View. Also, we will include a method which gives us handy access to a list of the Ids of the items where the Selected property is true:

The People Selection View Model:
publicclass PeopleSelectionViewModel
{
public List<SelectPersonEditorViewModel> People { get; set; }
public PeopleSelectionViewModel()
    {
this.People = new List<SelectPersonEditorViewModel>();
    }
public IEnumerable<int> getSelectedIds()
    {
// Return an Enumerable containing the Id's of the selected people:
return (from p inthis.People where p.Selected select p.Id).ToList();
    }
}

 

As convoluted as this may sound, what we do is use the PeopleSelectionViewModel to wrap an IEnumerable of SelectPersonEditorViewModel items, and pass this to out primary view. Now let's take a look at the views we will need to make this work.

Create an Editor View Template

Here is where we start to bring this all together. As mentioned previously, we need a custom Editor View Template in order to pass our individually selectable row data to and from our main view. We can locate individual Editor Templates in a folder named EditorTemplates nested in either the Folder specific to the main view in question (in this case, Views => People => EditorTemplates) or within the Views => Shared folder (which then makes the Editor Template available to the entire application).

In the current case, we will add an EditorTemplates folder to the Views => People folder, since this is a rather special purpose template, specific to the People controller.

Add an Editor Templates Folder to the Appropriate Directory:

add-the-editor-templates-folder

Now, let's add our Editor Template View. Right-Click on the new folder, add a new View, and name it very specifically SelectPersonEditorViewModel, so that the name matches the name of the actual View Model we created earlier. Then add the following code:

The Select Person Editor View Model View:
@model AspNetCheckedListExample.Models.SelectPersonEditorViewModel
<tr>
<td style="text-align:center">
        @Html.CheckBoxFor(model => model.Selected)
</td>
<td>
        @Html.DisplayFor(model => model.Name)
</td>
<td>
        @Html.HiddenFor(model => model.Id)
</td>
<td>
        @Html.ActionLink("Edit", "Edit", new { id = Model.Id }) |
        @Html.ActionLink("Details", "Details", new { id = Model.Id }) |
        @Html.ActionLink("Delete", "Delete", new { id = Model.Id })
</td>
</tr>

 

The Editor Template View above provides display items for the relevant model data, as well as en editor for the checkbox, and a hidden element for the mode.Id property. We will use this Id property, and the value of the Selected property to determine which people the user has selected for additional processing when the list of View Models is returned from the view in an Http POST request.

Adding the Index View

Also in the Views => People folder, add a new Empty View named Index. This is the main View we will use to contain our table of People. Once the view has been added, add the following code:

The People Index View:
@model AspNetCheckedListExample.Models.PeopleSelectionViewModel
@{
    ViewBag.Title = "People";
}
<h2>People</h2>
@using (Html.BeginForm("SubmitSelected", "People", FormMethod.Post, new { encType = "multipart/form-data", name = "myform" }))
{
<table class="table">
<tr>
<th>
                Select
</th>
<th>
                Name
</th>
<th></th>
</tr>
        @Html.EditorFor(model => model.People)
</table>
<hr />
<br />
<input type="submit" name="operation" id="submit" value="Submit Selected" />
}

 

As you can see in the above, we have created a basic HTML Form, and within that have defined a table. The table headers are clearly laid out within this view, but we can see that where we would ordinarily populate our rows using a foreach() construct, here we simply specify @Html.EditorFor() and specify the the IEnumerable<SelectPersonEditorViewModel> represented by the People property of our Model.

Tying it all together - the People Controller

Of course, none of this means anything without the Controller to govern the interaction of our Views and Models. For our ultra-simple project, we are going to define an ultra-simple controller, which will include only two Action methods: Index, and SubmitSelected.

Add a controller to the controllers folder, appropriately named PeopleController, and add the following code:

The People Controller Class:
publicclass PeopleController : Controller
{
    TestData Db = new TestData();
public ActionResult Index()
    {
        var model = new PeopleSelectionViewModel();
foreach(var person in Db.People)
        {
            var editorViewModel = new SelectPersonEditorViewModel()
            {
                Id = person.Id,
                Name = string.Format("{0} {1}", person.firstName, person.LastName),
                Selected = true
            };
            model.People.Add(editorViewModel);
        }
return View(model);
    }
    [HttpPost]
public ActionResult SubmitSelected(PeopleSelectionViewModel model)
    {
// get the ids of the items selected:
        var selectedIds = model.getSelectedIds();
// Use the ids to retrieve the records for the selected people
// from the database:
        var selectedPeople = from x in Db.People
                             where selectedIds.Contains(x.Id)
                             select x;
// Process according to your requirements:
foreach(var person in selectedPeople)
        {
            System.Diagnostics.Debug.WriteLine(
string.Format("{0} {1}", person.firstName, person.LastName));
        }
// Redirect somewhere meaningful (probably to somewhere showing 
// the results of your processing):
return RedirectToAction("Index");
    }
}

 

In our Index method, we pull a List of Person instances from our database, and for each instance of Person we create an instance of SelectPersonEditorViewModel. Note that for our example application, I have decided that the default value of the Selected property will be set to true, as it is most likely the user will process the entire list.

We then load each instance of SelectPersonEditorViewModel into our PeopleSelectionViewModel, and then pass this to the Index View itself.

Processing the Selection Results in the Submit Selected Method

When the user submits the form represented in our index view, the model is returned in the POST request body. Note the we needed to decorate the SubmitSelected method with the [HttpPost] attribute. We then use the getSelectedItems() method we added to our PeopleSelectionViewModel to return a list of the selected Ids, and use the Ids to query the appropriate records from our database.

In the example, I obviously am just writing some output to the console to demonstrate that we have, indeed, returned only the selected items from our View. Here is where you would add your own processing, according to the needs of your business model.

Adding a Select All Checkbox to Toggle Selected Status

Commonly in this scenario, we would want to afford our user the ability to "Select All" the items in the list, or "Select None" of the items. The JQuery code for this has been added to the project source, and you can read more about how to do this in Adding a Select All Checkbox to a Checklist Table Using JQuery.

Modifications to Make the Demo Project Run (not required)

In order make the example project work, I went ahead and made the following modifications to the rest of the standard MVC project. The following is not really related to creating a table with checkboxes, but might help if you are following along and building this out from scratch instead of cloning the project from Github.

Make the Index View of the People the Default Home Page

In the <body> section of the shared _Layout.cshtml file (Views => Shared => _Layout.cshtml) I removed the tabs for Home, Contact, and About, and added a tab for People, pointing at the Index method of the People Controller. The revised <body> section now looks like this:

Modified Body Section of _Layout.cshtml:
<body>
<div class="navbar navbar-inverse navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
                @Html.ActionLink("Application name", "Index", "People", null, new { @class = "navbar-brand" })
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink("People", "Index", "People")</li>
</ul>
                @Html.Partial("_LoginPartial")
</div>
</div>
</div>
<div class="container body-content">
        @RenderBody()
<hr />
<footer>
<p>&copy; @DateTime.Now.Year - My ASP.NET Application</p>
</footer>
</div>
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @RenderSection("scripts", required: false)
</body>

 

Update the Route Configuration

Also, I modified the Route.config so that the default route points to the Index method of the People Controller, instead of Home. The modified Route mapping looks like this:

Modified Route Mapping:
routes.MapRoute(
    name: "Default",
    url: "{controller}/{action}/{id}",
    defaults: new { controller = "People", action = "Index", id = UrlParameter.Optional }
);

 

Remove Unneeded Controllers and Views

For the demo project, I had no need for the extra controllers and views included with the default MVC project, so I removed them. We can delete the Home Controller, as well as the Home folder in the Views directory.

Additional Resources and Items of Interest


Send Email to Selected Recipients from your ASP.NET MVC Web Application Part I

$
0
0

message-in-a-bottle.-500I recently had to throw together an application for work allow users to send email to recipients selected from a list. The application in question is used to manage attendees at trainings, and, when the training is complete, send an email to each attendee containing a link to download a personalized certificate.

In this article we will look at a building out a simplified version of the mail-sending component.

The goal here is two-fold. For one, if you have a specific need to send email from your web application, this will at least get you pointed in the right direction. Also, I am going to attempt to look at the specific problems we need to solve, and some possible approaches to solving them.

Image by Sergio Quesada | Some Rights Reserved

Because this article got a little long, I am breaking it up into two parts. in this first installment, we will examine our requirements, and build out our basic application structure, including necessary Models, Views, and creating our database using EF Migrations.

In the next installment, we will build out the SendMail() controller action, and create the additional pieces needed to send a personalized message to multiple recipients from within our MVC application.

You can follow along with the article, and you can also grab the source from my Github repo.

The Problem

Again, I have simplified things here to keep focused. Our example application has the following requirements:

  • Access to the application is restricted to authenticated, internal users only.
  • The application should be able to maintain a database of Mail Recipients, and allow the basic CRUD operations to be performed.
  • Users should be able to update the list of mail recipients, then select one or more to which they wish to send a personalized email, and, er, send email to those recipients.
  • The mail sent should contain a personalized message for each recipient, based on a template of some sort.
  • The application should track the messages sent, and display the most recent date that each recipient was sent a mail message.

Like I said, pretty simple. In fact, so simple that in and of itself, the application we create here is not good for much except demonstrating the concepts involved. But I will endeavor to point out where functionality might be added to make a real-world implementation more functional.

All right, let’s get started, and look at our requirements one at a time.

Create an ASP.NET Application with Restricted, Internal-Only Access

We need an ASP.NET MVC application which is essentially closed to all but authenticated (logged-in) users authorized by our application administrator. We have looked at this in a previous post, in which we created an MVC 5 application, removed all of the external registration options, and implemented some basic Role-Based Authentication. In that post, we also extended the basic ASP.NET Identity model to include some custom user properties such as email addresses.

In building out the example project for this post, I simply cloned the ASP.NET Role-Based Security Example project and used that as my starting point for this example. Once I had cloned the basic project, I had to go through the relatively painless (but never-the-less annoying) process of renaming the VS project, namespaces, and such.

If you are following along instead of simply cloning the source for this article, you will need to do the same. Also, don’t go and run the EF Migrations to build out the database yet. We will be adding to our models, and updating migrations can be painful.

Note that if you clone the either the source for this project, or the original ASP.NET Role-Based Security Example project, you will need to Enable Nuget Package Restore in order to get things working properly (you should have this set up anyway!).

Pull the Db Context Out into its Own Class

We are already utilizing a Code-First approach here from setting up our custom Identity Management. We will go ahead and continue along those lines. However, we want to do a little housekeeping first.

Our original project (and indeed, the default ASP.NET MVC Project) is set up so that the Entity Framework ApplicationDbContext is tucked into a file named IdentityModels.cs. ApplicationDbContext is the primary point of data access for our application, and I don’t like it tucked into the IdentityModels.cs file. So the first thing I am going to do is move it our of there and into its own class.

Here is what the IdentityModels.cs file looks like before we modify the code:

The Identity Models Code File Before Modification:
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
namespace AspNetRoleBasedSecurity.Models
{
publicclass ApplicationUser : IdentityUser
    {
        [Required]
publicstring FirstName { get; set; }
        [Required]
publicstring LastName { get; set; }
        [Required]
publicstring Email { get; set; }
    }
publicclass ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
public ApplicationDbContext()
            : base("DefaultConnection")
        {
        }
    }
publicclass IdentityManager
    {
publicbool RoleExists(string name)
        {
            var rm = new RoleManager<IdentityRole>(
new RoleStore<IdentityRole>(new ApplicationDbContext()));
return rm.RoleExists(name);
        }
// ...
// A bunch of other code related to Identity Management . . .
// ...
    }
}

 

As we can see, right there in the middle is our ApplicationDbContext class. Let’s pull that out into its own class file, just so we can keep better track of things. Add a new class, name it ApplicationDbContext.cs, then add the following code (we need to bring all the required namespaces along with us):

The new ApplicationDbContext Class File:
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
namespace AspNetEmailExample.Models
{
publicclass ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
public ApplicationDbContext()
            : base("DefaultConnection2")
        {
        }
    }
}

 

Adding Required Model Classes

Now, let’s get down to business. First, our requirements indicate that we should probably have a MailRecipient model, to persist our target mail recipient data. Also, it looks like we will need a SentMail model, since we are also going to be persisting a record of each mail sent. Each sent mail will refer to a parent MailRecipient, and each MailRecipient will have zero or more SentMail items.

Following is the code for a basic Mail Recipient model class:

Create the Mail Recipient Model

The Mail Recipient Model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
namespace AspNetEmailExample.Models
{
public partial class MailRecipient
    {
public MailRecipient()
        {
this.SentMails = new HashSet<SentMail>();
        }
        [Key]
        [Required]
publicint MailRecipientId { get; set; }
        [Required]
publicstring LastName { get; set; }
        [Required]
publicstring FirstName { get; set; }
publicstring Email { get; set; }
publicstring Company { get; set; }
publicstring FullName
        {
get
            {
returnstring.Format("{0} {1}", this.FirstName, this.LastName);
            }
        }
public DateTime? getLastEmailDate()
        {
            var top = (from m inthis.SentMails
                       orderby m.SentDate descending
                       select m).Take(1);
if (top.Count() > 0)
            {
return top.ElementAt(0).SentDate;
            }
else
            {
returnnull;
            }
        }
publicvirtual ICollection<SentMail> SentMails { get; set; }
    }
}

 

In the above notice that we have added a method, getLastEmailDate(). This is going to provide for our requirement that we display the last date on which a particular recipient was sent mail. We have also added a FullName property, which return a concatenation of the first and last names of the recipient as a convenience. Also note, we have included the appropriate attribute decorations so that Entity Framework can build out our database based on this model.

Create the Sent Mail Model

Now we need to build out the SentMail model (another fairly simple exercise):

The Sent Mail Model:
namespace AspNetEmailExample.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
public partial class SentMail
    {
        [Key]
        [Required]
publicint MailId { get; set; }
        [Required]
publicint MailRecipientId { get; set; }
        [Required]
publicstring SentToMail { get; set; }
        [Required]
publicstring SentFromMail { get; set; }
        [Required]
public System.DateTime SentDate { get; set; }
publicvirtual MailRecipient Recipient { get; set; }
    }
}

 

Once again, we have made sure to add all of the EF-required attribute decorations for a successful code-first scaffolding.

Create the Mail Recipients Controller and Associated Views

In thinking through what we need our simple application to do, our requirements indicate that, at a minimum, we need to be able to:

  • View a list of mail recipients, and select one or more specific recipients to send a personalized message to
  • Add new recipients to the database
  • Edit/Update recipient information
  • Delete recipients from the database
  • Send Email to the recipients selected by the user.

So we need a controller with the following methods:

  • Index (Displays a list of recipients with a checkbox to select for mailing)
  • Create
  • Edit
  • Delete
  • SendMail

We can let Visual Studio do a lot of the heavy lifting for us here, although we will need to modify the generated code afterwards. But let’s go ahead an build out our controller and views using VS and Entity Framework.

First, right click on the Controllers folder, and select Add => Controller. Then, from the dialog, select MVC 5 Controller with Views, using Entity Framework:

Add the Mail Recipients Controller Using Entity Framework:

Add Scaffold

Next, Name the Controller MailRecipientsController, and select the MailRecipient model from the dropdown list of available models. Select the ApplicationDbContext class from the dropdown for the Data Context class, and hit Add:

Set the Controller Configuration:

Add Controller

Once the controller and Views are created, open the newly created MailRecipientsController class. You should see code which looks something like this (I removed all the comments VS automatically includes, because I don’t like the noise in my code):

The Generated MailRecipientsController:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
using System.Net;
using System.Web;
using System.Web.Mvc;
using AspNetEmailExample.Models;
namespace AspNetEmailExample.Controllers
{
publicclass MailRecipientsController : Controller
    {
private ApplicationDbContext db = new ApplicationDbContext();
public async Task<ActionResult> Index()
        {
return View(await db.MailRecipients.ToListAsync());
        }
public async Task<ActionResult> Details(int? id)
        {
if (id == null)
            {
returnnew HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            MailRecipient mailrecipient = await db.MailRecipients.FindAsync(id);
if (mailrecipient == null)
            {
return HttpNotFound();
            }
return View(mailrecipient);
        }
public ActionResult Create()
        {
return View();
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include="MailRecipientId,LastName,FirstName,Email,Company")] MailRecipient mailrecipient)
        {
if (ModelState.IsValid)
            {
                db.MailRecipients.Add(mailrecipient);
                await db.SaveChangesAsync();
return RedirectToAction("Index");
            }
return View(mailrecipient);
        }
public async Task<ActionResult> Edit(int? id)
        {
if (id == null)
            {
returnnew HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            MailRecipient mailrecipient = await db.MailRecipients.FindAsync(id);
if (mailrecipient == null)
            {
return HttpNotFound();
            }
return View(mailrecipient);
        }
        [HttpPost]
        [ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Include="MailRecipientId,LastName,FirstName,Email,Company")] MailRecipient mailrecipient)
        {
if (ModelState.IsValid)
            {
                db.Entry(mailrecipient).State = EntityState.Modified;
                await db.SaveChangesAsync();
return RedirectToAction("Index");
            }
return View(mailrecipient);
        }
public async Task<ActionResult> Delete(int? id)
        {
if (id == null)
            {
returnnew HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            MailRecipient mailrecipient = await db.MailRecipients.FindAsync(id);
if (mailrecipient == null)
            {
return HttpNotFound();
            }
return View(mailrecipient);
        }
        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
public async Task<ActionResult> DeleteConfirmed(int id)
        {
            MailRecipient mailrecipient = await db.MailRecipients.FindAsync(id);
            db.MailRecipients.Remove(mailrecipient);
            await db.SaveChangesAsync();
return RedirectToAction("Index");
        }
protectedoverridevoid Dispose(bool disposing)
        {
if (disposing)
            {
                db.Dispose();
            }
base.Dispose(disposing);
        }
    }
}

 

As we can see, VS created some basic code for four of the five controller methods we need. However, they don’t quite do what we need yet. As is most often the case, we will need to adapt them to our needs a little bit.

Modifying the Create Method for the Mail Recipients Controller

We will start by addressing the Create method. The first thing I notice is that the generated code includes a Binding for the MailRecipientId property of our model. The problem here is that MailRecipientId will be an auto-incrementing integer field in our database, and will not be a field on our input form. So we need to remove MailRecipientId from the Binding in the Create method signature.

Also note that, because access to our site is restricted to authenticated, internal users only, we have added the [Authorize] attribute to both overrides for the Create method. In fact, we will be adding this important attribute to all of the methods on MailRecipientController.

The modified Create method should now look like this:

The Modified Create Method for Mail Recipient Controller:
[Authorize]
public ActionResult Create()
{
return View();
}
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include="LastName,FirstName,Email,Company")] MailRecipient mailrecipient)
{
if (ModelState.IsValid)
    {
        db.MailRecipients.Add(mailrecipient);
        await db.SaveChangesAsync();
return RedirectToAction("Index");
    }
return View(mailrecipient);
}

 

The Edit and Delete methods work decently “out of the box” so far as our application is concerned, although we do still want to add the [Authorize] attribute to each of these as well.

Now let’s take a look at our Index method.

Editor Templates and View Models for the Index Method

We need to make some significant changes to the way our Index controller method works, but first, we need a few more items. In our Index View, we want to display a list of potential Mail Recipients, along with checkboxes for each allowing us to select one or more recipients from the list for a personalized email. For this, we will need to pass a list of Mail recipients to our view.

To achieve this we will need to implement an HTML form including a table with checkboxes, as well as the required View Models and Editor Templates this requires. For more detail on this, see Display an HTML Table with Checkboxes in ASP.NET.

As we learned in the article linked above, we will need to create a custom Editor Template in order to display rows in our table with checkboxes associated with the data in each row. Also, we will need an Editor View Model to match the Editor Template, and one more View Model to wrap the Editor View Model items in. Confused? It's not too bad.

First, let's create what we will call the SelectRecipientEditorViewModel. This will provide the model for our row Editor Template, and will include a Selected property to represent the checked status of the checkbox for each row.

Add a new class to the Models folder, and name it SelectRecipientEditorViewModel. Then add the following code:

The Select Recipient Editor View Model Class:
publicclass SelectRecipientEditorViewModel
{
publicbool Selected { get; set; }
public SelectRecipientEditorViewModel() { }
publicint MailRecipientId { get; set; }
publicstring FullName { get; set; }
publicstring Company { get; set; }
publicstring Email { get; set; }
publicstring LastMailedDate { get; set; }
}

 

Next, we need a "wrapper" class, MailRecipientsViewModel to contain our list of recipients. Add another new class to the Models folder:

The Mail Recipients View Model Class:
publicclass MailRecipientsViewModel
{
public List<SelectRecipientEditorViewModel> MailRecipients { get; set; }
public MailRecipientsViewModel()
    {
this.MailRecipients = new List<SelectRecipientEditorViewModel>();
    }
public IEnumerable<int> getSelectedRecipientIds()
    {
return (from r inthis.MailRecipients 
                where r.Selected 
                select r.MailRecipientId).ToList();
    }
}

 

In a manner similar to the linked article about displaying checkboxes, we have added a method to our class which will conveniently return an IEnumerable<int> containing the Id's of the selected items in our table.

Now, we just need an Editor Template for our SelectRecipientEditorViewModel. If you don't already see a subfolder in the Views => Shared directory, add a new View named EditorTemplates.The naming here is critical, so no typos. Now, add a new empty View to Views => Shared => EditorTemplates, named SelectRecipientEditorViewModel (yes, it is named exactly the same as the View Model we created, except for the .cshtml suffix on the view file vs. the .cs suffix on the Model class file). Then add the following code:

The Select Recipient Editor Template View:
@model AspNetEmailExample.Models.SelectRecipientEditorViewModel
<tr>
<td style="text-align:center">
        @Html.CheckBoxFor(model => model.Selected)
</td>
<td>
        @Html.DisplayFor(model => model.FullName)
</td>
<td>
        @Html.DisplayFor(model => model.Company)
</td>
<td>
        @Html.DisplayFor(model => model.Email)
</td>
<td>
        @Html.DisplayFor(model => model.LastMailedDate)
</td>
<td>
        @Html.HiddenFor(model => model.MailRecipientId)
</td>
<td>
        @Html.ActionLink("Edit", "Edit", new { id = Model.MailRecipientId }) |
        @Html.ActionLink("Details", "Details", new { id = Model.MailRecipientId }) |
        @Html.ActionLink("Delete", "Delete", new { id = Model.MailRecipientId })
</td>
</tr>

 

For more details on how this works, see the linked article above.

Modifying the Code for the Index Controller Method

Now we have what we need to modify our Index() controller method, such that is can pass to the Index view a list of selectable mail recipient data. Update the code for the Index() method on MailRecipientsController to match the following:

Modified Code for the Index Controller Method:
[Authorize]
public async Task<ActionResult> Index()
{
    var model = new MailRecipientsViewModel();
// Get a list of all the recipients:
    var recipients = await db.MailRecipients.ToListAsync();
foreach(var item in recipients)
    {
// Put the relevant data into the ViewModel:
        var newRecipient = new SelectRecipientEditorViewModel()
        {
            MailRecipientId = item.MailRecipientId,
            FullName = item.FullName,
            Company = item.Company,
            Email = item.Email,
            LastMailedDate = item.getLastEmailDate().HasValue ? item.getLastEmailDate().Value.ToShortDateString() : "",
            Selected = true
        };
// Add to the list contained by the "wrapper" ViewModel:
        model.MailRecipients.Add(newRecipient);
    }
// Pass to the view and return:
return View(model);
}

 

As we can see, the Index() method now retrieves a list of all the recipients from the data store, iterates over the list, and creates an instance of SelectRecipientsViewModel for each (in this case, setting the Selected property to true for each, although this is optional. The default value of Selected may vary with the needs of your application).

Add a Send Mail Method Stub to the Index Controller

For the moment, we won't get into the nitty gritty of actually sending email - We'll look at that in the next post. For now, let's just add a method stub with a delay that simulates a long-running process such as sending a bunch of email, then returns the refreshed Index view again.

Add the following code to the end of the Index Controller:

Code Stub for the Send Mail Method:
[HttpPost]
[Authorize]
public ActionResult SendMail(MailRecipientsViewModel recipients)
{
// Mail-sending code will happen here . . .
    System.Threading.Thread.Sleep(2000);
return RedirectToAction("Index");
}

 

The Index View - Selecting Recipients and Sending Email

Here again, we refer to concepts discussed in Display an HTML Table with Checkboxes in ASP.NET. We need to modify the Index.cshtml file created by Visual Studio to accommodate our special need for checkboxes. We will also be adding a Select All option, and a "Send Mail" button to, well, send email once we have selected some recipients form the list.

The generated code produced by VS looks like this:

Code Generated by Visual Studio for the Index View:
@model IEnumerable<AspNetEmailExample.Models.MailRecipient>
@{
    ViewBag.Title = "Index";
}
<h2>Index</h2>
<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
<tr>
<th>
            @Html.DisplayNameFor(model => model.LastName)
</th>
<th>
            @Html.DisplayNameFor(model => model.FirstName)
</th>
<th>
            @Html.DisplayNameFor(model => model.Email)
</th>
<th>
            @Html.DisplayNameFor(model => model.Company)
</th>
<th></th>
</tr>
@foreach (var item in Model) {
<tr>
<td>
            @Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
            @Html.DisplayFor(modelItem => item.FirstName)
</td>
<td>
            @Html.DisplayFor(modelItem => item.Email)
</td>
<td>
            @Html.DisplayFor(modelItem => item.Company)
</td>
<td>
            @Html.ActionLink("Edit", "Edit", new { id=item.MailRecipientId }) |
            @Html.ActionLink("Details", "Details", new { id=item.MailRecipientId }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.MailRecipientId })
</td>
</tr>
}
</table>

 

This view expects a Model of type MailRecipient, and will display an overly cluttered table, with a column for every property. Instead, we would like to simplify the table layout, as well as consume a different Model - our MailRecipientViewModel, and the list of potential recipients it contains.

Replace the code above with the following:

Replacement Code for the Index View:
@model AspNetEmailExample.Models.MailRecipientsViewModel
@{
    ViewBag.Title = "Email";
}
<h2>Send Email to Selected Recipients</h2>
<p>
    @Html.ActionLink("Create New", "Create")
</p>
@using (Html.BeginForm("SendMail", "MailRecipients", FormMethod.Post, new { encType = "multipart/form-data", name = "myform" }))
{
// Add a "Check All" checkbox above the table:
<div>
<input type="checkbox" id="checkall" /><span>Check All</span>
</div>
// Wrap the table in a named <div> so we can refer to it from JQuery:
<div id="checkboxes">
<table class="table">
<tr>
<th>
                    @*This column will contain our checkboxes:*@
                    Select
</th>
<th>
                    @*This column will now hold a concatenation of the first/last names:*@
                    Name
</th>
<th>
                    Company
</th>
<th>
                    Email
</th>
<th>
                    Last Sent
</th>
<th></th>
</tr>
            @* Our Table rows will be populated in the EditorTemplate: *@
            @Html.EditorFor(model => model.MailRecipients)
</table>
</div>
<hr />
<br />
//Add a submit button to the bottom of the form:
<input type="submit" name="operation" id="email" value="Email Selected" />
}
<div id="divProcessing">
<p>Processing, please wait . . . <img src="../../Content/ajax-loader.gif"></p>
</div>
    @section Scripts {
        @Scripts.Render("~/bundles/jqueryval")
<script type="text/javascript">
            function toggleChecked(status) {
                $("#checkboxes input").each(function () {
                    $(this).prop("checked", status);
                });
            }
            $(document).ready(function () {
// Grab a reference to the checkall checkbox:
                var checkAllBox = $("#checkall");
                var divProcessing = $("#divProcessing");
// Hide the animated Gif when page loads:
                divProcessing.hide();
                checkAllBox.prop('checked', true);
// Attach a handler for the checkAllBox click event:
                checkAllBox.click(function () {
                    var status = checkAllBox.prop('checked');
                    toggleChecked(status);
                });
                $('#email').click(function () {
// Required hack to get animated gif to run in IE:
                    setTimeout(function () {
                        divProcessing.show();
                    }, 100);
                    $('myform').submit();
                });
            });
</script>
    }

 

In the code above, we have made substantial changes to the View. First, the View is now based on our MailRecipientsViewModel, instead of a List<MailRecipient> as before. Also, we have added a new table column layout, with a checkbox as the first column, and a single column for displaying the name.

Instead of iterating over each item in a list to populate our table, we simply pass the MailRecipients property of our MailRecipientsViewModel to our Editor Template, which then takes care of rendering our list.

Also notice, we have added some JavaScript and additional HTML elements to provide a "Check All" checkbox at the top of the table. In addition, we have added some extra elements and some JavaScript to display an animated GIF Busy indicator after the user clicks the submit button to send mail (see the linked article for more about how this works - you will need to add an GIF to the Content folder if you did not clone this project from source).

Finally, note where we declare our HTML form. Specifically, in the BeginForm() method we set our submit arguments to create an HTTP POST request against the SendMail() action of the MailRecipients controller.

Add a Mail Recipients Tab to the Layout

Now, so that we can get to our Mail Recipient functionality, let's add a tab to the main layout. Open the Views => Shared => _Layout.cshtml file. In the middle of the file, add a tab that links to the Index method of our MailRecipientsController:

Add a Tab for Mail Recipients:
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li>@Html.ActionLink("Home", "Index", "Home")</li>
<li>@Html.ActionLink("About", "About", "Home")</li>
<li>@Html.ActionLink("Contact", "Contact", "Home")</li>
<li>@Html.ActionLink("Admin", "Index", "Account")</li>
</ul>
    @Html.Partial("_LoginPartial")
</div>

 

For now, we are just leaving the default Home/About/Contact views as they are. in reality, we would remove these and replace them with our own and probably use [Authorize] to restrict access to most of our application in keeping with our requirement for internal, authorized-only access.

** If you cloned the project from Github, these are already removed**

At this point, we have everything we need to run our project, make sure everything is working properly, and make any adjustments if needed. Well, almost. First, we need to run Entity Framework Migrations to create our database.

Run EF Migrations to Create the Database

If you have cloned this project from Github, you should be all set to go. The migration is already defined, although you may want to open the Configuration file in the Migrations folder and swap out my sample user name for your own. Simply open the Package Manager Console and do:

Update Database -F

 

I have found I sometimes need to do this twice to get the Seed() method to run.

If you need more information on this part of the process, see Configuring Db Connection and Code-First Migration for Identity Accounts in ASP.NET MVC 5 and Visual Studio 2013.

Running the Application

Ok. If everything has gone well, you should be able to run the application. You will need to log in using the user name and password you created in the Seed() method, otherwise you won't be able to access the Mail Recipients tab (remember, this is an internal access application, with no public registration).

The Application Main Page at Startup:

application-start-page

Once logged in, navigate to the Mail Recipients tab. Not much to see here yet, as we have no recipients yet:

The Mail Recipients Page (Empty):

application-mail-page

Click the Create New link to add a recipient. When done, you should see something like this:

The Mail Recipients Page (with data):

application-after-add-recipient

Now, if we click the Email Selected button, we should see our "busy" spinner while our method stub runs down the clock on the Thread.Sleep() call we are using to mock sending our emails:

Pretending to Send Mail:

application-sending

Because this is getting a little long, I'm going to address the actual mail sending in the next post.

For now, we have built out a fairly basic front-end, providing a surface for the user interaction. At the same time, we have addressed our requirement the mailing list portion of the application be restricted to internal, authorized access.

Up Next: Adding the Mail Sender and Filling in the SendMail() Method

Additional Resources and Items of Interest

Send Email to Selected Recipients from your ASP.NET MVC Web Application Part II

$
0
0

tidepool-320This is the second part of an article demonstrating how to build out an application for sending personalized email to recipients selected from a list.

In the first part, we put together the basic structure of our ASP.NET MVC application, according to a simple list of requirements.

Now, we will add the email functionality, such that the user may select one or more recipients from a list using checkboxes, then generate and send a personalize email to each.

Image by Sergio Quesada | Some Rights Reserved

Review Part I --> Send Email to Selected Recipients from your ASP.NET MVC Web Application

The Send Mail Method

In the previous post, we created a stub for the SendMail method and threw some code in there to emulate a long-running process such as sending a list of emails:

The Send Mail Method Stub:
[HttpPost]
[Authorize]
public ActionResult SendMail(MailRecipientsViewModel recipients)
{
// Mail-sending code will happen here . . .
    System.Threading.Thread.Sleep(2000);
return RedirectToAction("Index");
}

 

We did this so that we could run our application, and all the front end functionality would work as expected. Now let's see what we need to do in order to actually send some mail.

The Problem to Solve

Let's take a look at what we need to accomplish here by breaking the problem into steps. In order to send a personalized message to each of the recipients selected by the user, we need to:

  • Retrieve the recipient data for each of the recipients selected by the user
  • Compose a message personalized for each recipient by inserting the recipient's name into some sort of message template, and addressed to the recipient's email.
  • Retrieve the current User's email address to use as the "From" email address, and the current user's name to use in the signature
  • Represent the above as a "Message" which can be aggregated into a list of messages to be sent, addressed to each recipient.
  • Add a record to the SentMail table representing the key points for each email sent (basically a log)
  • When sending is complete, redirect to the Index page, and refresh, displaying updated records which include a filed for the date mail was most recently sent to each recipient.
  • Pass the list to some sort of Mail Sender, which can iterate over the list and send each message.

Pseudo-Code the New and Improved SendMail Method

Given the above, it looks like we might want our SendMail method to do something like this:

Pseudo-Code for Steps in Sending Mail:
[HttpPost]
[Authorize]
public ActionResult SendMail(MailRecipientsViewModel recipients)
{
// Retrieve the ids of the recipients selected:
// Grab the recipient records:
// Build the message container for each:
// Send the mail:
// Save a record of each mail sent:
// Reload the index form:
}

So, lets make that happen!

In order to keep our Action method clean and simple, we are going to make each of these steps a call to a locally defined method. The code for each step could then also be easily moved out of the controller into another class or classes, depending on the needs of your application and/or fussiness about how much work should be done within the controller itself. We aren't going to get all pedantic about it here.

Retrieve the Selected Recipients

We can start by thinking about what we actually receive in the recipients argument passed to SendMail from the HTTP request body. We will get back an instance of MailRecipientsViewModel, which provides a method getSelectedRecipientIds(). This returns an IEnumerable<int> representing the Ids of the recipients selected by the user on our form.

Reviewing our MailRecipientsViewModel class:

The Get Selected Recipient Ids Method:
publicclass MailRecipientsViewModel
{
public List<SelectRecipientEditorViewModel> MailRecipients { get; set; }
public MailRecipientsViewModel()
    {
this.MailRecipients = new List<SelectRecipientEditorViewModel>();
    }
public IEnumerable<int> getSelectedRecipientIds()
    {
return (from r inthis.MailRecipients 
                where r.Selected 
                select r.MailRecipientId).ToList();
    }
}

 

Private Implementation Code

Now That we have our Ids, lets fill in the rest of our private helper methods. Add the following code the the controller after the SendMail stub:

Adding Code to the Controller to Implement the Send Mail Method:
IEnumerable<MailRecipient> LoadRecipientsFromIds(IEnumerable<int> selectedIds)
{
    var selectedMailRecipients = from r in db.MailRecipients
                                 where selectedIds.Contains(r.MailRecipientId)
                                 select r;
return selectedMailRecipients;
}
IEnumerable<Message> createRecipientMailMessages(
    IEnumerable<MailRecipient> selectedMailRecipients)
{
    var messageContainers = new List<Message>();
    var currentUser = db.Users.Find(User.Identity.GetUserId());
foreach (var recipient in selectedMailRecipients)
    {
        var msg = new Message()
        {
            Recipient = recipient,
            User = currentUser,
            Subject = string.Format("Welcome, {0}", recipient.FullName),
            MessageBody = this.getMessageText(recipient, currentUser)
        };
        messageContainers.Add(msg);
    }
return messageContainers;
}
void SaveSentMail(IEnumerable<SentMail> sentMessages)
{
foreach (var sent in sentMessages)
    {
        db.SentMails.Add(sent);
        db.SaveChanges();
    }
}
string getMessageText(MailRecipient recipient, ApplicationUser user)
{
return""
    + string.Format("Dear {0}, ", recipient.FullName) + Environment.NewLine
    + "Thank you for your interest in our latest product. "
    + "Please feel free to contact me for more information!"
    + Environment.NewLine
    + Environment.NewLine
    + "Sincerely, "
    + Environment.NewLine
    + string.Format("{0} {1}", user.FirstName, user.LastName);
}

 

Abstracting an Email Message - the Message Class

In the code above, we see we create an instance of a class Message. This is another Model we need to add to our Models folder. We are using the Message class to represent everything needed to send an email:

Add the following class to the Models folder:

The Message Class:
publicclass Message
{
public MailRecipient Recipient { get; set; }
public ApplicationUser User { get; set; }
publicstring Subject { get; set; }
publicstring MessageBody { get; set; }
}

 

Also, in the createRecipientMailMessages method, we grab the current logged-in User with the following call:

Get the Current Logged-in User:
var currentUser = db.Users.Find(User.Identity.GetUserId());

 

In order for this to work we need to add a reference to the Microsoft.AspNet.Identity namespace in the usings at the top of our code file, or this code won't work.

Call Implementation Code from Send Mail Method

Now that we have broken out each of our steps into discrete private method calls, we can call these from within the SendMail method:

[HttpPost]
[Authorize]
public ActionResult SendMail(MailRecipientsViewModel recipients)
{
// Retrieve the ids of the recipients selected:
    var selectedIds = recipients.getSelectedRecipientIds();
// Grab the recipient records:
    var selectedMailRecipients = this.LoadRecipientsFromIds(selectedIds);
// Build the message container for each:
    var messageContainers = this.createRecipientMailMessages(selectedMailRecipients);
// Send the mail:
    var sender = new MailSender();
    var sent = sender.SendMail(messageContainers);
// Save a record of each mail sent:
this.SaveSentMail(sent);
// Reload the index form:
return RedirectToAction("Index");
}

 

In the above, we have working code for everything except step 4, in which we initialize an instance of MailSender, and then actually send the mail. Now we get to the nitty-gritty of our application.

The Mail Sender Class

In our SendMail code, we build up a list of Message instances, which we then pass to a new class we haven't looked at yet - the MailSender class.

Add a new class to the project, name it MailSender, and paste in the following code:

The Mail Sender Class:
publicclass MailSender
{
public IEnumerable<SentMail> SendMail(IEnumerable<Message> mailMessages)
    {
        var output = new List<SentMail>();
// Modify this to suit your business case:
string mailUser = "youremail@outlook.com";
string mailUserPwd = "password";
        SmtpClient client = new SmtpClient("smtp.host.com");
        client.Port = 587;
        client.DeliveryMethod = SmtpDeliveryMethod.Network;
        client.UseDefaultCredentials = false;
        System.Net.NetworkCredential credentials = 
new System.Net.NetworkCredential(mailUser, mailUserPwd);
        client.EnableSsl = true;
        client.Credentials = credentials;
foreach (var msg in mailMessages)
        {
            var mail = new MailMessage(msg.User.Email.Trim(), msg.Recipient.Email.Trim());
            mail.Subject = msg.Subject;
            mail.Body = msg.MessageBody;
try
            {
                client.Send(mail);
                var sentMessage = new SentMail()
                {
                    MailRecipientId = msg.Recipient.MailRecipientId,
                    SentToMail = msg.Recipient.Email,
                    SentFromMail = msg.User.Email,
                    SentDate = DateTime.Now
                };
                output.Add(sentMessage);
            }
catch (Exception ex)
            {
throw ex;
// Or, more likely, do some logging or something
            }
        }
return output;
    }
}

 

You will need to make sure you import the following namespaces for the code to work:

Required Namespaces for the Mail Sender Class:
using AspNetEmailExample.Models;
using System;
using System.Collections.Generic;
using System.Net.Mail;

 

Mail Client Configuration Settings

I discuss the details of setting up the mail client for Outlook.com or Gmail in another post. For most mail hosts, the client configuration should resemble the above. However, pay attention. For one, as discussed in the post linked above, if you have some sort of two-step authorization in place on your mail host, you will likely need to use an Application-Specific Password for this to work. Also note, you can send mail using your Outlook.com account as a host, but unlike most other mail hosting accounts, the Outlook.com host name for SMTP is:

smtp-mail.outlook.com

Whereas Gmail is simply:

smtp.gmail.com

 

For other mail hosts, you may have to experiment a little, or consult the provider documentation.

Walking Through the Execution of the Send Mail Method

With all of our pieces in place, we can now walk through the execution of SendMail() and take an high-level look at what is going on in all these small, refactored methods, and how they align with the steps we defined to send mail to each recipient,

First, we use our list of selected Ids to retrieve a corresponding list of fully instantiated recipient instances. This list is then returned to the call in SentMail, whereupon it is passed to the createMailRecipientMessages() method.

This next method iterates the list of recipients, and creates a new Message instance for each, supplying the property values needed to send an email. Two of these, the User and MessageBody properties, involve additional calls. Retrieving the current user requires a call into the Microsoft.AspNet.Identity library.

The getMessageText method, from which we retrieve the actual text for each mail message, represents a crude, "just make it work" implementation of what, in a real application, should probably be a template-based system. I have kept things simple here, but in reality we would probably like to be able to retrieve a message template from some resource or another, and populate the template properly from code without having to re-write and recompile.

How you implement this would depend significantly on your application requirements and is beyond the scope of this article (this article is already long, considering the topic is not all that advanced!). If you have either questions, or brilliant ideas for implementing such a system in your own application, I would love to hear either. This might become the topic of another article.

Once we have constructed our list of Message objects, we pass that to the MailSender.SendMail method, and, well, send the damn mail. We can see that each Message object is used to create a System.Net.Mail.MailMessage object, which is then sent using our properly configured mail client.

Once each Message is sent, we create a SentMail object, and then return the list of List<SentMail> back to the SendMail controller method, at which point we persist the SentMail objects, and redirect back to the Index method.

Running the Project and Sending Mail

Now, we likely have our test data from before, when we entered some examples to test out our front-end. You may want to go ahead and change the example.com email addresses to an actual mail account you can access, to ensure all is working properly. Then, run the application, log in, and try the "Email Selected" button again. you may want to deselect one or two of the potential recipients in the list, just to see the difference:

Try Sending Some Mail:

try-sending-mail-before

This time, we should see our "Busy" spinner for a moment, and then be redirected back to a refreshed Index view, now updated with the last date we sent mail to the selected recipients:

The Updated Index View After Sending Mail:

try-sending-mail-after

As we can see, the two items selected for sending email have now been updated with a Last Sent date.

What Went Wrong?

If you have been following along, building this out as you go, and something doesn't work, I strongly recommend cloning the example project from source and trying to run that. For what appears to be a simple application, there are actually a lot of places where I may have missed some small but critical item in posting the code here on the blog. I've tried to balance providing everything you need to build this out yourself with keeping the article length manageable (and still semi-failed on the length part!).

If you clone from source, and still have an issue, please do describe it in the comments section and/or shoot me an email. Also, if you see somewhere I have made a mistake, or taken the "dumb way" to doing something, I am ALL EARS.

I've tried to combine providing a useful tutorial on the mechanics of sending mail from an application, with my own steps in thinking through the problem. Obviously, some of the content here is aimed at folks new to ASP.NET and/or Web Development in general.

Thanks for reading, and your feedback is always appreciated.

Additional Resources and Items of Interest

ASP.NET MVC 5 Identity: Extending and Modifying Roles

$
0
0

 

security-cam-by-hay-kranenIn a recent article I took a rather long look at extending the ASP.NET 5 Identity model, adding some custom properties to the basic IdentityUser class, and also some basic role-based identity management. We did not discuss modifying, extending, or working directly with Roles, beyond seeding the database with some very basic roles with which to manage application access.

Extending the basic ASP.NET IdentityRole class, and working directly with roles from an administrative perspective, requires some careful consideration, and no small amount of work-around code-wise.

Image by Hay Kranen | Some Rights Reserved

There are two reasons this is so:

  • As we saw in the previous article, the ASP.NET team made it fairly easy to extend IdentityUser, and also to get some basic Role management happening.
  • When roles are used to enforce access restrictions within our application, they are basically hard-coded, usually via the [Authorize] attribute. Giving application administrators the ability to add, modify, and delete roles is of limited use if they cannot also modify the access permissions afforded by [Authorize] .

The above notwithstanding, sometimes we might wish to add some properties to our basic roles, such as a brief description.

In this post we will see how we can extend the IdentityRole class, by adding an additional property. We will also add the basic ability to create, edit, and delete roles, and what all is involved with that, despite the fact that any advantage to adding or removing roles is limited by the hard-coded [Authorize] permissions within our application.

In the next post, ASP.NET MVC 5 Identity: Implementing Group-Based Permissions Management,  look at working around the limitations of the Role/[Authorize] model to create a more finely-grained role-based access control system.

UPDATE: 2/24/2014 - Thanks to Code Project user Budoray for catching some typos in the code. The EditRoleViewModel and RoleViewModel classes were referenced incorrectly  in a number of places, preventing the project from building properly. Fixed!

In an upcoming post we will look at working around the limitations of the Role/[Authorize] model to create a more finely-grained role-based access control system.

Getting Started - Building on Previous Work

We have laid the groundwork for what we will be doing in the previous article on Extending Identity Accounts, so we will clone that project and build on top of the work already done. In that project, we:

  • Created a restricted, internal access MVC site.
  • Removed extraneous code related to social media account log-ins, and other features we don't need.
  • Extended the IdentityUser class to include some additional properties, such as first/last names, and email addresses.
  • Added the ability to assign users to pre-defined roles which govern access to various functionality within our application.

Clone the Source

You can clone the original project and follow along, or you can grab the finished project from my Github repo. To get the original project and build along with this article, clone the source from:

If you want to check out the finished project, clone the source from:

First, a Little Refactoring

In the original project, I had left all of the Identity-related models in the single file created with the default project template. Before getting started here, I pulled each class out into its own code file. Also, we will be re-building our database and migrations.

After cloning the source, I deleted the existing Migration (not the Migrations folder, just the Migration file within named 201311110510410_Init.cs. We will keep the Migrations/Configuration.cs file as we will be building out on that as we go.

If you are following along, note that I also renamed the project and solution, namespaces, and such, as I am going to push this project up to Github separately from the original.

There is plenty of room for additional cleanup in this project, but for now, it will do. Let's get started.

Getting Started

Previously, we were able to define the model class ApplicationUser, which extended the Identity class IdentityUser, run EF Migrations, and with relative ease swap it with IdentityUser in all the areas of our application which previously consumed IdentityUser. Things are not so simple, however, when it comes to extending IdentityRole.

IdentityRole forms a core component in the authorization mechanism for an ASP.NET application. For this reason, we might expect the Identity system to be resistant to casual modification of the IdentityRole class itself, and perhaps equally importantly, the manner in which the rest of the identity system accepts derivatives. So we need to find a way to accomplish what we wish to achieve without compromising the integrity of the Identity mechanism, or those components downstream which may depend upon an instance of IdentityRole to get the job done.

First off, let's take a look at our existing ApplicationDbContext class:

The ApplicationDbContext Class:
publicclass ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }
}

 

In the above, we can see we are inheriting from the class IdentityDbContext<TUser>, which allows us to specify a custom type, so long as that type is derived from IdentityUser. So it appears that the Identity system generally provides a built-in mechanism for extending IdentityUser.

Is there a similar path for extending IdentityRole?

Turns out there is. Sort of.

Extending the Identity Role Class

First, of course, we need to create our derived class, ApplicationRole. Add the following class to the Models folder:

The Application Role Class:
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
namespace AspNetExtendingIdentityRoles.Models
{
publicclass ApplicationRole : IdentityRole
    {
public ApplicationRole() : base() { }
public ApplicationRole(string name, string description) : base(name)
        {
this.Description = description;
        }
publicvirtualstring Description { get; set; }
    }
}

 

As we can see, we have created a derived class and implemented a simple new Description property, along with a new overridden constructor.

Next, we need modify our ApplicationDbContext so that, when we run EF Migrations, our database will reflect the proper modeling. Open the ApplicationDbContext class, and add the following override for the OnModelCreating method:

Add These Namespaces to the Top of your Code File:
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.AspNet.Identity;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System.Data.Entity;
using System;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration;
using System.Data.Entity.ModelConfiguration.Configuration;
using System.Data.Entity.Validation;
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;

 

Then add the following Code to the ApplicationDbContext class:

Overriding the OnModelCreating method:
protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder)
{
if (modelBuilder == null)
    {
thrownew ArgumentNullException("modelBuilder");
    }
// Keep this:
    modelBuilder.Entity<IdentityUser>().ToTable("AspNetUsers");
// Change TUser to ApplicationUser everywhere else - 
// IdentityUser and ApplicationUser essentially 'share' the AspNetUsers Table in the database:
    EntityTypeConfiguration<ApplicationUser> table = 
        modelBuilder.Entity<ApplicationUser>().ToTable("AspNetUsers");
    table.Property((ApplicationUser u) => u.UserName).IsRequired();
// EF won't let us swap out IdentityUserRole for ApplicationUserRole here:
    modelBuilder.Entity<ApplicationUser>().HasMany<IdentityUserRole>((ApplicationUser u) => u.Roles);
    modelBuilder.Entity<IdentityUserRole>().HasKey((IdentityUserRole r) => 
new { UserId = r.UserId, RoleId = r.RoleId }).ToTable("AspNetUserRoles");
// Leave this alone:
    EntityTypeConfiguration<IdentityUserLogin> entityTypeConfiguration = 
        modelBuilder.Entity<IdentityUserLogin>().HasKey((IdentityUserLogin l) => 
new { UserId = l.UserId, LoginProvider = l.LoginProvider, ProviderKey 
            	= l.ProviderKey }).ToTable("AspNetUserLogins");
    entityTypeConfiguration.HasRequired<IdentityUser>((IdentityUserLogin u) => u.User);
    EntityTypeConfiguration<IdentityUserClaim> table1 = 
    	modelBuilder.Entity<IdentityUserClaim>().ToTable("AspNetUserClaims");
    table1.HasRequired<IdentityUser>((IdentityUserClaim u) => u.User);
// Add this, so that IdentityRole can share a table with ApplicationRole:
    modelBuilder.Entity<IdentityRole>().ToTable("AspNetRoles");
// Change these from IdentityRole to ApplicationRole:
    EntityTypeConfiguration<ApplicationRole> entityTypeConfiguration1 = 
    	modelBuilder.Entity<ApplicationRole>().ToTable("AspNetRoles");
    entityTypeConfiguration1.Property((ApplicationRole r) => r.Name).IsRequired();
}

          In the above, we are basically telling Entity Framework how to model our inheritance structure into the database.

          We can, however, tell EF to model our database in such a way that both of our derived classes can also utilize the same tables, and in fact extend them to include our custom fields. Notice how, in the code above, we first tell the modelBuilder to point the IdentityUser class at the table "AspNetUsers", and then also tell it to point ApplicationUser at the same table?

          We do the same thing later with ApplicationRole.

          As you can see, there is actually no getting away from either the IdentityUser or IdentityRole classes - both are used by the Identity system under the covers.  We are simply taking advantage of polymorphism such that, at the level of our application, we are able to use our derived classes, while down below, Identity recognizes them as their base implementations. We will see how this affects our application shortly.

          Update the Identity Manager Class

          Now, however, we can replace IdentityRole with ApplicationRole in most of the rest of our application, and begin using our new Description property.

          We will begin with our IdentityManager class. I did a little refactoring here while I was at it, so if you are following along, this code will look a little different than what you will find in the original project. Just paste this code in (but make sure your namespaces match!). I also added a few new using's at the top of the code file.

          Modified Identity Manager Class:
          publicclass IdentityManager
          {
          // Swap ApplicationRole for IdentityRole:
              RoleManager<ApplicationRole> _roleManager = new RoleManager<ApplicationRole>(
          new RoleStore<ApplicationRole>(new ApplicationDbContext()));
              UserManager<ApplicationUser> _userManager = new UserManager<ApplicationUser>(
          new UserStore<ApplicationUser>(new ApplicationDbContext()));
              ApplicationDbContext _db = new ApplicationDbContext();
          publicbool RoleExists(string name)
              {
          return _roleManager.RoleExists(name);
              }
          publicbool CreateRole(string name, string description = "")
              {
          // Swap ApplicationRole for IdentityRole:
                  var idResult = _roleManager.Create(new ApplicationRole(name, description));
          return idResult.Succeeded;
              }
          publicbool CreateUser(ApplicationUser user, string password)
              {
                  var idResult = _userManager.Create(user, password);
          return idResult.Succeeded;
              }
          publicbool AddUserToRole(string userId, string roleName)
              {
                  var idResult = _userManager.AddToRole(userId, roleName);
          return idResult.Succeeded;
              }
          publicvoid ClearUserRoles(string userId)
              {
                  var user = _userManager.FindById(userId);
                  var currentRoles = new List<IdentityUserRole>();
                  currentRoles.AddRange(user.Roles);
          foreach (var role in currentRoles)
                  {
                      _userManager.RemoveFromRole(userId, role.Role.Name);
                  }
              }
          }

           

          Update the Add Users and Roles Method in Migrations Configuration

          We will also want to update our AddUsersAndRoles() method, which is called by the Seed() method in the Configuration file for EF Migrations. We want to seed the database with Roles which use our new extended properties:

          The Updated Add Users and Groups Method:
          bool AddUserAndRoles()
          {
          bool success = false;
              var idManager = new IdentityManager();
          // Add the Description as an argument:
              success = idManager.CreateRole("Admin", "Global Access");
          if (!success == true) return success;
          // Add the Description as an argument:
              success = idManager.CreateRole("CanEdit", "Edit existing records");
          if (!success == true) return success;
          // Add the Description as an argument:
              success = idManager.CreateRole("User", "Restricted to business domain activity");
          if (!success) return success;
          // While you're at it, change this to your own log-in:
              var newUser = new ApplicationUser()
              {
                  UserName = "jatten",
                  FirstName = "John",
                  LastName = "Atten",
                  Email = "jatten@typecastexception.com"
              };
          // Be careful here - you  will need to use a password which will 
          // be valid under the password rules for the application, 
          // or the process will abort:
              success = idManager.CreateUser(newUser, "Password1");
          if (!success) return success;
              success = idManager.AddUserToRole(newUser.Id, "Admin");
          if (!success) return success;
              success = idManager.AddUserToRole(newUser.Id, "CanEdit");
          if (!success) return success;
              success = idManager.AddUserToRole(newUser.Id, "User");
          if (!success) return success;
          return success;
          }

           

          All we really did here was pass an additional argument to the CreateRole() method, such the the seed roles will exhibit our new property.

          Now, we need to make a few adjustments to our Account Controller, View Models, and Views.

          Update the Select Role Editor View Model

          In the previous article, we created a SelectRoleEditorViewModel which accepted an instance of IdentityRole as a constructor argument. We need to modify the code here in order to accommodate any new properties we added when we extended IdentityRole. For our example, we just added a single new property, so this is pretty painless:

          The Modified SelectRoleEditorViewModel:
          publicclass SelectRoleEditorViewModel
          {
          public SelectRoleEditorViewModel() { }
          // Update this to accept an argument of type ApplicationRole:
          public SelectRoleEditorViewModel(ApplicationRole role)
              {
          this.RoleName = role.Name;
          // Assign the new Descrption property:
          this.Description = role.Description;
              }
          publicbool Selected { get; set; }
              [Required]
          publicstring RoleName { get; set; }
          // Add the new Description property:
          publicstring Description { get; set; }
          }

           

          Update the Corresponding Editor View Model

          Recall that, in order to display the list of roles with checkboxes as an HTML form from which we can return the selection choices made by the user, we needed to define an EditorViewModel.cshtml file which corresponded to our EditorViewModel class. In Views/Shared/EditorViewModels open SelectRoleEditorViewModel.cshtml and make the following changes:

          The Modified SelectRoleEditorViewModel:
          @model AspNetExtendingIdentityRoles.Models.SelectRoleEditorViewModel
          @Html.HiddenFor(model => model.RoleName)
          <tr>
          <td style="text-align:center">
                  @Html.CheckBoxFor(model => model.Selected)
          </td>
          <td style="padding-right:20px">
                  @Html.DisplayFor(model => model.RoleName)
          </td>
          <td style="padding-right:20px">
                  @Html.DisplayFor(model => model.Description)
          </td>
          </tr>

          Again, all we needed to do in the above was add a table data element for the new property.

          Update the User Roles View

          To this point, we actually only have one View which displays our Roles - the UserRoles view, where we assign users to one or more Roles within our application. Once again, we really just need to add a table Header element to represent our new Description property:

          The Updated UserRoles View:

          @model AspNetExtendingIdentityRoles.Models.SelectUserRolesViewModel
          @{
              ViewBag.Title = "User Roles";
          }
          <h2>Roles for user @Html.DisplayFor(model => model.UserName)</h2>
          <hr />
          @using (Html.BeginForm("UserRoles", "Account", FormMethod.Post, new { encType = "multipart/form-data", name = "myform" }))
          {
              @Html.AntiForgeryToken()
          <div class="form-horizontal">
                  @Html.ValidationSummary(true)
          <div class="form-group">
          <div class="col-md-10">
                          @Html.HiddenFor(model => model.UserName)
          </div>
          </div>
          <h4>Select Role Assignments</h4>
          <br />
          <hr />
          <table>
          <tr>
          <th>
                              Select
          </th>
          <th>
                              Role
          </th>
          <th>
                              Description
          </th>
          </tr>
                      @Html.EditorFor(model => model.Roles)
          </table>
          <br />
          <hr />
          <div class="form-group">
          <div class="col-md-offset-2 col-md-10">
          <input type="submit" value="Save" class="btn btn-default" />
          </div>
          </div>
          </div>
          }
          <div>
              @Html.ActionLink("Back to List", "Index")
          </div>

           

          Do We Really Want to Edit Roles?

          Here is where you may want to think things through a little bit. Consider - the main reason for utilizing Roles within an ASP.NET application is to manage authorization. We do this by hard-coding access permissions to controllers and/or specific methods through the [Authorize] attribute.

          If we make roles createable/editable/deletable, what can possibly go wrong? Well, a lot. First off, if we deploy our application, then add a role, we really have no way to make use of the new role with respect to the security concerns listed above, without re-compiling and re-deploying our application after adding the new role to whichever methods we hope to secure with it.

          The same is true if we change a role name, or worse, delete a role. In fact, an ambitious admin user could lock themselves out of the application altogether!

          However, there are some cases where we might want to have this functionality anyway. If, for example, you are the developer, and you add some new role permissions via [Authorize] to an existing application, it is more convenient to add the new roles to the database through the front-end than by either manually adding to the database, or re-seeding the application using Migrations. Also, if your application is in production, the re-running migrations really isn't an option anyway.

          In any case, a number of commentors on my previous post expressed the desire to be able to modify or remove roles, so let's plow ahead!

          Why Not? Adding the Roles Controller

          Once I had everything built out, and I went to use Visual Studio's built-in scaffolding to add a new Roles controller, I ran into some issues. Apparently, by extending IdentityRole the way we have, Entity Framework has some trouble scaffolding up a new controller based on the ApplicationRole model (EF detects some ambiguity between IdentityRole and ApplicationRole). I didn't spend too much time wrestling with this - it was easy enough to code yup a simple CRUD controller the old-fashioned way, so that's what I did.

          Here is my hand-rolled RolesController, ready for action:

          The Roles Controller:
          using AspNetExtendingIdentityRoles.Models;
          using System.Collections.Generic;
          using System.Data.Entity;
          using System.Linq;
          using System.Net;
          using System.Web.Mvc;
          namespace AspNetExtendingIdentityRoles.Controllers
          {
          publicclass RolesController : Controller
              {
          private ApplicationDbContext _db = new ApplicationDbContext();
          public ActionResult Index()
                  {
                      var rolesList = new List<RoleViewModel>();
          foreach(var role in _db.Roles)
                      {
                          var roleModel = new RoleViewModel(role);
                          rolesList.Add(roleModel);
                      }
          return View(rolesList);
                  }
                  [Authorize(Roles = "Admin")]
          public ActionResult Create(string message = "")
                  {
                      ViewBag.Message = message;
          return View();
                  }
                  [HttpPost]
                  [Authorize(Roles = "Admin")]
          public ActionResult Create([Bind(Include = 
          "RoleName,Description")]RoleViewModel model)
                  {
          string message = "That role name has already been used";
          if (ModelState.IsValid)
                      {
                          var role = new ApplicationRole(model.RoleName, model.Description);
                          var idManager = new IdentityManager();
          if(idManager.RoleExists(model.RoleName))
                          {
          return View(message);
                          }
          else
                          {
                              idManager.CreateRole(model.RoleName, model.Description);
          return RedirectToAction("Index", "Account");
                          }
                      }
          return View();
                  }
                  [Authorize(Roles = "Admin")]
          public ActionResult Edit(string id)
                  {
          // It's actually the Role.Name tucked into the id param:
                      var role = _db.Roles.First(r => r.Name == id);
                      var roleModel = new EditRoleViewModel(role);
          return View(roleModel);
                  }
                  [HttpPost]
                  [Authorize(Roles = "Admin")]
          public ActionResult Edit([Bind(Include = 
          "RoleName,OriginalRoleName,Description")] EditRoleViewModel model)
                  {
          if (ModelState.IsValid)
                      {
                          var role = _db.Roles.First(r => r.Name == model.OriginalRoleName);
                          role.Name = model.RoleName;
                          role.Description = model.Description;
                          _db.Entry(role).State = EntityState.Modified;
                          _db.SaveChanges();
          return RedirectToAction("Index");
                      }
          return View(model);
                  }
                  [Authorize(Roles = "Admin")]
          public ActionResult Delete(string id)
                  {
          if (id == null)
                      {
          returnnew HttpStatusCodeResult(HttpStatusCode.BadRequest);
                      }
                      var role = _db.Roles.First(r => r.Name == id);
                      var model = new RoleViewModel(role);
          if (role == null)
                      {
          return HttpNotFound();
                      }
          return View(model);
                  }
                  [Authorize(Roles = "Admin")]
                  [HttpPost, ActionName("Delete")]
          public ActionResult DeleteConfirmed(string id)
                  {
                      var role = _db.Roles.First(r => r.Name == id);
                      var idManager = new IdentityManager();
                      idManager.DeleteRole(role.Id);
          return RedirectToAction("Index");
                  }
              }
          }

           

          In the above, notice that when we go to delete a role, we make a call out to our IdentityManager class to a method named DeleteRole(). Why the complexity, John? Why not just delete the role from the datastore directly?

          There's a reason for that . . .

          About Deleting Roles

          Think about it. If you have one or more users assigned to a role, when you delete that role, you want to remove the users from the role first. Otherwise you will run into foreign key issues in your database which will not let you delete the role.

          So, we need to add a couple important methods to IdentityManager.

          Adding a Delete Role Method to Identity Manager

          Clearly, in order to delete roles, we first need to remove any users from that role first. Then we can delete the role. However, we have to employ a slight hack to do this, because the Identity framework does not actually implement a RemoveRole() method out of the box. Oh, it's there - you can find it if you look hard enough. The RoleStore<IRole> class actually defines a DeleteAsync method. However, it throws a "Not Implemented" exception.

          Here is how I worked around the issue. Add the following two methods to the IdentityManager class:

          Adding DeleteRole and RemoveFromRole Methods to Identity Manager Class:
          publicvoid RemoveFromRole(string userId, string roleName)
          {
              _userManager.RemoveFromRole(userId, roleName);
          }
          publicvoid DeleteRole(string roleId)
          {
              var roleUsers = _db.Users.Where(u => u.Roles.Any(r => r.RoleId == roleId));
              var role = _db.Roles.Find(roleId);
          foreach (var user in roleUsers)
              {
          this.RemoveFromRole(user.Id, role.Name);
              }
              _db.Roles.Remove(role);
              _db.SaveChanges();
          }

           

          First, notice how we pass in a simple RoleId instead of an instance or ApplicationRole? This is because, in order for the Remove(role) method to operate properly, the role passed in as an argument must be from the same ApplicationDbContext instance, which would not be the case if we were to pass one in from our controller.

          Also notice, before calling _db.Roles.Remove(role) , we retrieve the collection of users who are role members, and remove them. This solves the foreign key relationship problem, and prevents orphan records in the AspNetUserRoles table in our database.

          ViewModels and Views for the Roles Controller

          Notice in our controller, we make use of two new View Models, the RoleViewModel, and the EditRoleViewModel. I went ahead and added these to the AccountViewModels.cs file. The code is as follows:

          The RoleViewModel and EditRoleViewModel Classes:
          publicclass RoleViewModel
          {
          publicstring RoleName { get; set; }
          publicstring Description { get; set; }
          public RoleViewModel() { }
          public RoleViewModel(ApplicationRole role)
              {
          this.RoleName = role.Name;
          this.Description = role.Description;
              }
          }
          publicclass EditRoleViewModel
          {
          publicstring OriginalRoleName { get; set; }
          publicstring RoleName { get; set; }
          publicstring Description { get; set; }
          public EditRoleViewModel() { }
          public EditRoleViewModel(ApplicationRole role)
              {
          this.OriginalRoleName = role.Name;
          this.RoleName = role.Name;
          this.Description = role.Description;
              }
          }

           

          Views Used by the Role Controller

          The Views used by the RolesController were created by simply right-clicking on the associated Controller method and selecting "Add View." I'm including it here for completeness, but there is nothing revolutionary going on here. The code for each follows.

          The Index Role View

          Displays a list of the Roles, along with Action Links to Edit or Delete.

          Code for the Create Role View:
          @model IEnumerable<AspNetExtendingIdentityRoles.Models.RoleViewModel>
          @{
              ViewBag.Title = "Application Roles";
          }
          <h2>Application Roles</h2>
          <p>
              @Html.ActionLink("Create New", "Create")
          </p>
          <table class="table">
          <tr>
          <th>
                      @Html.DisplayNameFor(model => model.RoleName)
          </th>
          <th>
                      @Html.DisplayNameFor(model => model.Description)
          </th>
          <th></th>
          </tr>
          @foreach (var item in Model) {
          <tr>
          <td>
                      @Html.DisplayFor(modelItem => item.RoleName)
          </td>
          <td>
                      @Html.DisplayFor(modelItem => item.Description)
          </td>
          <td>
                      @Html.ActionLink("Edit", "Edit", new { id = item.RoleName }) |
                      @Html.ActionLink("Delete", "Delete", new { id = item.RoleName })
          </td>
          </tr>
          }
          </table>

           

          The Create Role View

          Obviously, affords creation of new Roles.

          Code for the Create Role View:
          @model AspNetExtendingIdentityRoles.Models.RoleViewModel
          @{
              ViewBag.Title = "Create";
          }
          <h2>Create Role</h2>
          @using (Html.BeginForm()) 
          {
              @Html.AntiForgeryToken()
          <div class="form-horizontal">
          <h4>RoleViewModel</h4>
          <hr />
                  @Html.ValidationSummary(true)
                  @if(ViewBag.Message != "")
                  {
          <p style="color: red">ViewBag.Message</p>
                  }
          <div class="form-group">
                      @Html.LabelFor(model => model.RoleName, 
          new { @class = "control-label col-md-2" })
          <div class="col-md-10">
                          @Html.EditorFor(model => model.RoleName)
                          @Html.ValidationMessageFor(model => model.RoleName)
          </div>
          </div>
          <div class="form-group">
                      @Html.LabelFor(model => model.Description, 
          new { @class = "control-label col-md-2" })
          <div class="col-md-10">
                          @Html.EditorFor(model => model.Description)
                          @Html.ValidationMessageFor(model => model.Description)
          </div>
          </div>
          <div class="form-group">
          <div class="col-md-offset-2 col-md-10">
          <input type="submit" value="Create" class="btn btn-default" />
          </div>
          </div>
          </div>
          }
          <div>
              @Html.ActionLink("Back to List", "Index")
          </div>
          @section Scripts {
              @Scripts.Render("~/bundles/jqueryval")
          }

           

          The Edit Roles View

          For, um, editing Roles . ..

          Code for the Edit Roles View:
          @model AspNetExtendingIdentityRoles.Models.EditRoleViewModel
          @{
              ViewBag.Title = "Edit";
          }
          <h2>Edit</h2>
          @using (Html.BeginForm())
          {
              @Html.AntiForgeryToken()
          <div class="form-horizontal">
          <h4>EditRoleViewModel</h4>
          <hr />
                  @Html.ValidationSummary(true)
                  @*Hide the original name away for later:*@
                  @Html.HiddenFor(model => model.OriginalRoleName)
          <div class="form-group">
                      @Html.LabelFor(model => model.RoleName, 
          new { @class = "control-label col-md-2" })
          <div class="col-md-10">
                          @Html.EditorFor(model => model.RoleName)
                          @Html.ValidationMessageFor(model => model.RoleName)
          </div>
          </div>
          <div class="form-group">
                      @Html.LabelFor(model => model.Description, 
          new { @class = "control-label col-md-2" })
          <div class="col-md-10">
                          @Html.EditorFor(model => model.Description)
                          @Html.ValidationMessageFor(model => model.Description)
          </div>
          </div>
          <div class="form-group">
          <div class="col-md-offset-2 col-md-10">
          <input type="submit" value="Save" class="btn btn-default" />
          </div>
          </div>
          </div>
          }
          <div>
              @Html.ActionLink("Back to List", "Index")
          </div>
          @section Scripts {
              @Scripts.Render("~/bundles/jqueryval")
          }

           

          The Delete Roles View

          Code for the Delete Roles View:
          @model AspNetExtendingIdentityRoles.Models.RoleViewModel
          @{
              ViewBag.Title = "Delete";
          }
          <h2>Delete</h2>
          <h3>Are you sure you want to delete this?</h3>
          <div>
          <h4>RolesViewModel</h4>
          <hr />
          <dl class="dl-horizontal">
          <dt>
                      @Html.DisplayNameFor(model => model.RoleName)
          </dt>
          <dd>
                      @Html.DisplayFor(model => model.RoleName)
          </dd>
          <dt>
                      @Html.DisplayNameFor(model => model.Description)
          </dt>
          <dd>
                      @Html.DisplayFor(model => model.Description)
          </dd>
          </dl>
              @using (Html.BeginForm()) {
                  @Html.AntiForgeryToken()
          <div class="form-actions no-color">
          <input type="submit" value="Delete" class="btn btn-default" /> |
                      @Html.ActionLink("Back to List", "Index")
          </div>
              }
          </div>

           

          Modify _Layout.cshtml to Add Users and Roles Links

          I went ahead and modified the _Layout.cshtml file, changed what was the "Admin" link to simply "Users," and added a new "Roles" link as follows:

          Modify _Layout.cshtml:
          <div class="navbar-collapse collapse">
          <ul class="nav navbar-nav">
          <li>@Html.ActionLink("Home", "Index", "Home")</li>
          <li>@Html.ActionLink("About", "About", "Home")</li>
          <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
          <li>@Html.ActionLink("Users", "Index", "Account")</li>
          <li>@Html.ActionLink("Roles", "Index", "Roles")</li>
          </ul>
              @Html.Partial("_LoginPartial")
          </div>

           

          Run Migrations and Build out the Database

          Ok, you should now be able to add a new migration, run it, and build out the database. If you are new to EF Migrations, you may want to review Configuring Entity Framework Migrations. In this case, the cloned project already has migrations enabled, so all we need to do is Build, then type into the Package Manager Console:

          Add New Migration (Delete the previous Migration file, or choose a new name)
          Add-Migration init

           

          Then, if all went well with that,

          Update The Database:
          Update-Database

           

          Running the Application

          If all went well (and I haven't missed anything in this post!) we should be able to run our application, log in, and navigate to the "Users" tab. Then, select the "Roles" link of the single user listed. You should see something similar to this:

          The User Roles View:user-roles-view

           

          We can see, our new property is evident.

          Some Thoughts in Closing

          This was a rather long article to implement some relatively minor functionality. However, we touched on some concepts which may prove helpful if you need just a little more from the new Identity system than is available straight out of the box. Also, I am setting the stage for the next article, where I will look at setting up "Role Groups" to which users may be assigned. In this scenario, we can use the built-in Roles to set pretty granular access permissions within our code, and then assign users to predefined "Groups" of such Role permissions, somewhat mimicking familiar domain permissions.

          Pay Attention When Messing with Auth and Security!

          I do my best when working with membership or Identity to stay within the bounds and mechanisms set up by the ASP.NET team. I am far, far from a security expert (I DID buy a book on it recently, though!), and those people know way more about creating a secure authorization system than I could ever hope to.

          In the preceding article on extending IdentityUser and adding roles to our application, we stayed well within the bounds of the intended uses of Identity. In this article, I ventured a little further afield, extending a class which it appears the ASP.NET team did not intend to be easily extended. Further, I implemented a means to create, edit, and delete roles at the application user level. I assume there are reasons such functionality was not built-in out of the box. Most likely for the reasons I discussed previously.

          That said, near as I can tell we have done nothing here to explicitly compromise the security of our application, or the integrity of the identity system. Our ApplicationRole class, through inheritance and polymorphism, is consumed properly by the internals of the ASP.NET Identity system, while delivering whatever additional properties we required.

          Got Any Thoughts? See Some Improvements?

          If you see where I have something wrong, or missed something while moving the code into this article, please do let me know in the comments, or shoot me an email at the address in the "About the Author" sidebar. If you have suggestions for improving on what you see here, please let me know, or submit a Pull Request on Github. I would love to incorporate any good ideas.

          Watch for the next article on implementing groups and permissions!

          Additional Resources and Items of Interest

          ASP.NET MVC 5 Identity: Implementing Group-Based Permissions Management Part I

          $
          0
          0

          leeds-castle-portcullis-500Over the course of several recent articles, we're examined various ways and means of working with and extending the ASP.NET Identity System. We've covered the basics of configuring the database connections and working with  the EF Code-First approach used by the Identity System, extending the core IdentityUser class to add our own custom properties and behaviors, such as email addresses, First/Last names, and such. While we did that, we also looked at utilizing the basic Role-based account management which comes with ASP.NET Identity out of the box.

          In the last post, we figured out how to extend the IdentityRole class, which took a little more doing than was required with IdentityUser.

          Image by Shaun Dunmall | Some Rights Reserved

          Here, we are going one step further, and building out a more advanced, "permissions management" model on top of the basic Users/Roles paradigm represented by the core ASP.NET Identity System out of the box.

          Before we go too much further, it bears mentioning that implementing a complex permissions management system is not a small undertaking. While the model we are about to look at is not overly difficult, managing a large number of granular permissions in the context of a web application could be. You will want to think hard and plan well before you implement something like this in a production site.

          With careful up-front planning, and a well-designed permission structure, you should be able to find a middle ground for your site between bloated, complex, and painful enterprise-type solutions such as Active Directory or Windows Authentication and the overly simple Identity management as it comes out of the box.

          More on this later. First, some background.

          Granular Management of Authorization Permissions - The Principle of Least Privilege

          Good security is designed around (among other things) the Principle of Least Privilege. That is, "in a particular abstraction layer of a computing environment, every module (such as a process, a user or a program depending on the subject) must be able to access only the information and resources that are necessary for its legitimate purpose"

          As we are well aware by now, the primary way we manage access to different functionality within our ASP.NET MVC application is through the [Authorize] attribute. We decorate specific controller methods with [Authorize] and define which roles can execute the method. For example, we may be building out a site for a business. Among other things, the site will likely contain any number of operational or business domains, such as Site Administration, Human Resources, Sales, Order Processing, and so on.

          A hypothetical PayrollController might contain, among others, the following methods:

          Methods from a hypothetical Payroll Controller:
          [Authorize(Roles = "HrAdmin, CanEnterPayroll")]
          [HttpPost]
          public ActionResult EnterPayroll(string id)
          {
          //  . . . Enter some payroll . . . 
          }
          [Authorize(Roles = "HrAdmin, CanEditPayroll, CanProcessPayroll")]
          [HttpPost]
          public ActionResult EditPayroll(string id)
          {
          //  . . . Edit existing payroll entries . . . 
          }
          [Authorize(Roles = "HrAdmin, CanProcessPayroll")]
          [HttpPost]
          public ActionResult ProcessPayroll(string id)
          {
          //  . . . Process payroll and cut checks . . . 
          }

           

          We infer from the above that the grunts who simply enter the payroll information have no business editing work already in the system. On the other hand, there are those in the company who may need to be able to edit existing payroll, which might include the managers of particular employees departments, the HR Manager themselves, and those whose job it is to process the payroll.

          The action of actually processing payroll and creating checks for payment is very restricted. Only the HR manager, and those members of the "ProcessPayroll" role are able to do this, and we can assume their number is few.

          Lastly, we see that the HrAdmin role has extensive privileges, including all of these functions, and also  presumable is able to act as the administrator within the Human Resources application Domain, assigning these and other domain permissions to the various users within the domain. 

          Limitations of Application Authorization Under Identity

          Under the current Identity system's out-of-the-box implementation (even with the ways in which we have extended it over these last few articles), We have Users, and Roles. Users are assigned to one or more roles as part of our security setup, and Admins are able to add or remove users from various roles.

          Role access to various application functionality is hard-coded into our application via [Authorize], so creating and modifying roles in production is of little value, unless we have implemented some other business reason for it.

          Also under the current system, each time we add a new user to the system, we need to assign individual roles specific to the user. This is not a big deal if our site includes (for example) "Admins", "Authors" and "Users." However, for a more complex site, with multiple business domains, and multiple users serving in multiple roles, this could become painful.

          When security administration becomes painful, we tend to default to time-saving behavior, such as ignoring the Principle of Least Privilege, and instead granting users broad permissions so we don't have to bother (at least, if we don't have a diligent system admin!).

          A Middle of the Road Solution

          In this article, we examine one possible manner of extending the Identity model to form a middle-of-the road solution. For applications of moderate complexity, which require a little more granularity in authorization permissions, but which may not warrant moving to a heavy-weight solutions such as Active Directory.

          I am proposing the addition of what appear to be authorization Groups to the identity mix. Groups are assigned various combinations of permissions, and Users are assigned to one or more groups.

          To do this, we will be creating a slight illusion. We will simply be treating what we currently recognize as Roles as, instead, Permissions. We will then create groups of these "Role-Permissions" and assign users to one or more groups. Behind the scenes, of course, we are still constrained by the essential elements of Identity; Users and Roles. We are also still limited by having to hard-code our "Permissions" into [Authorize] attributes. However, we can define these "Role-Permissions" at a fairly granular level now, because managing assignment of Role Permissions to users will be done by assigning Users to Groups, at which point such a user will assume all of the specific permissions of each particular Group.

          Building on Previous Work

          I started with the foundation we have built so far, by cloning the project from the last article where we extended our Roles by inheriting from IdentityRole. We didn't do anything earth-shaking in that, but we did get a closer look at how we might override the OnModelCreating() method of ApplicationDbContext and bend EF and the Identity framework to our will, without compromising the underlying security mechanisms created by the ASP.NET team.

          You can either do the same, and follow along as we walk through building this out, or you can clone the finished source from this article.

          Get the Original Source from Github:
          Get the Completed Source for this Article:

          As in previous articles, once I have cloned the initial source project, I renamed the solution files, namespaces, directory, and project files, since in my case, I will be pushing this up as a new project, not as new changes to the old.

          Next, delete the existing Migrations files (but not the Migrations folder, and not the Configuration.cs file). We will be adding to our Code-First model before we build the database, so we don't need these files anymore.

          Now, we're ready to get started.

          Adding the Group and ApplicationRoleGroup Models

          First, of course, we need our Group class. The Group class will represent a named group of roles, and therefore we consider that the Group class has a collection of roles.  However, since each Group can include zero or many roles, and each role can also belong to zero or many Groups, this will be a many-to-many mapping in our database. Therefore, we first need an intermediate object, ApplicationRoleGroup which maps the foreign keys in the many-to-many relationship.

          Add the following classes to the Models folder:

          The Application Role Group Model Class:
          using Microsoft.AspNet.Identity.EntityFramework;
          using Microsoft.AspNet.Identity;
          using System.ComponentModel.DataAnnotations;
          using System.Collections.Generic;
          namespace AspNetGroupBasedPermissions.Models
          {
          publicclass ApplicationRoleGroup
              {
          publicvirtualstring RoleId { get; set; }
          publicvirtualint GroupId { get; set; }
          publicvirtual ApplicationRole Role { get; set; }
          publicvirtual Group Group { get; set; }
              }
          }

           

          Then add the Group class as another new class in Models:

          The Group Class:
          using Microsoft.AspNet.Identity.EntityFramework;
          using Microsoft.AspNet.Identity;
          using System.ComponentModel.DataAnnotations;
          using System.Collections.Generic;
          namespace AspNetGroupBasedPermissions.Models
          {
          publicclass Group
              {
          public Group() {}
          public Group(string name) : this()
                  {
          this.Roles = new List<ApplicationRoleGroup>();
          this.Name = name;
                  }
                  [Key]
                  [Required]
          publicvirtualint Id { get; set; }
          publicvirtualstring Name { get; set; }
          publicvirtual ICollection<ApplicationRoleGroup> Roles { get; set; }
              }
          }

           

          Next, we need to create a similar many-to-many mapping model for ApplicationUser and Group. Once again, each user can have zero or many groups, and each group can have zero or many users. We already have our ApplicationUser class (although we need to modify it a little), but we need an ApplicationUserGroup class to complete the mapping.

          Add the ApplicationUserGroup Model

          Add the ApplicationUserGroup class to the Models folder:

          using Microsoft.AspNet.Identity.EntityFramework;
          using Microsoft.AspNet.Identity;
          using System.ComponentModel.DataAnnotations;
          using System.Collections.Generic;
          namespace AspNetGroupBasedPermissions.Models
          {
          publicclass ApplicationUserGroup
              {
                  [Required]
          publicvirtualstring UserId { get; set; }
                  [Required]
          publicvirtualint GroupId { get; set; }
          publicvirtual ApplicationUser User { get; set; }
          publicvirtual Group Group { get; set; }
              }
          }

           

          Next, we need to add a Groups Property to ApplicationUser, in such a manner that Entity Framework will understand and be able to use it to populate the groups when the property is accessed. This means we need to add a virtual property which returns a instance of ICollection<ApplicationUserGroup> when the property is accessed.

          Modify the existing ApplicationUser class as follows:

          Modified ApplicationUser Class:
          using Microsoft.AspNet.Identity.EntityFramework;
          using Microsoft.AspNet.Identity;
          using System.ComponentModel.DataAnnotations;
          using System.Collections.Generic;
          namespace AspNetGroupBasedPermissions.Models
          {
          publicclass ApplicationUser : IdentityUser
              {
          public ApplicationUser()
                      : base()
                  {
          this.Groups = new HashSet<ApplicationUserGroup>();
                  }
                  [Required]
          publicstring FirstName { get; set; }
                  [Required]
          publicstring LastName { get; set; }
                  [Required]
          publicstring Email { get; set; }
          publicvirtual ICollection<ApplicationUserGroup> Groups { get; set; }
              }
          }

           

          Update ApplicationDbContext to Reflect the New Model

          Now that we have extended our model somewhat, we need to update the OnModelCreating method of ApplicationDbContext so that EF can properly model our database, and work with our objects.

          ** This whole method becomes a little messy and cluttered, but a discerning read of the code reveals the gist of what is happening here. Don't worry too much about understanding the details of this code - just try to get a general picture of how it is mapping model entities to database tables. **

          Update the OnModelCreating() method of ApplicationDbContext as follows:

          Modified OnModelCreating Method for ApplicationDbContext:
          protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder)
          {
          if (modelBuilder == null)
              {
          thrownew ArgumentNullException("modelBuilder");
              }
          // Keep this:
              modelBuilder.Entity<IdentityUser>().ToTable("AspNetUsers");
          // Change TUser to ApplicationUser everywhere else - IdentityUser 
          // and ApplicationUser essentially 'share' the AspNetUsers Table in the database:
              EntityTypeConfiguration<ApplicationUser> table = 
                  modelBuilder.Entity<ApplicationUser>().ToTable("AspNetUsers");
              table.Property((ApplicationUser u) => u.UserName).IsRequired();
          // EF won't let us swap out IdentityUserRole for ApplicationUserRole here:
              modelBuilder.Entity<ApplicationUser>().HasMany<IdentityUserRole>((ApplicationUser u) => u.Roles);
              modelBuilder.Entity<IdentityUserRole>().HasKey((IdentityUserRole r) => 
          new { UserId = r.UserId, RoleId = r.RoleId }).ToTable("AspNetUserRoles");
          // Add the group stuff here:
              modelBuilder.Entity<ApplicationUser>().HasMany<ApplicationUserGroup>((ApplicationUser u) => u.Groups);
              modelBuilder.Entity<ApplicationUserGroup>().HasKey((ApplicationUserGroup r) => 
          new { UserId = r.UserId, GroupId = r.GroupId }).ToTable("ApplicationUserGroups");
          // And here:
              modelBuilder.Entity<Group>().HasMany<ApplicationRoleGroup>((Group g) => g.Roles);
              modelBuilder.Entity<ApplicationRoleGroup>().HasKey((ApplicationRoleGroup gr) => 
          new { RoleId = gr.RoleId, GroupId = gr.GroupId }).ToTable("ApplicationRoleGroups");
          // And Here:
              EntityTypeConfiguration<Group> groupsConfig = modelBuilder.Entity<Group>().ToTable("Groups");
              groupsConfig.Property((Group r) => r.Name).IsRequired();
          // Leave this alone:
              EntityTypeConfiguration<IdentityUserLogin> entityTypeConfiguration = 
                  modelBuilder.Entity<IdentityUserLogin>().HasKey((IdentityUserLogin l) => 
          new { UserId = l.UserId, LoginProvider = l.LoginProvider, ProviderKey = 
                          l.ProviderKey }).ToTable("AspNetUserLogins");
              entityTypeConfiguration.HasRequired<IdentityUser>((IdentityUserLogin u) => u.User);
              EntityTypeConfiguration<IdentityUserClaim> table1 = 
                  modelBuilder.Entity<IdentityUserClaim>().ToTable("AspNetUserClaims");
              table1.HasRequired<IdentityUser>((IdentityUserClaim u) => u.User);
          // Add this, so that IdentityRole can share a table with ApplicationRole:
              modelBuilder.Entity<IdentityRole>().ToTable("AspNetRoles");
          // Change these from IdentityRole to ApplicationRole:
              EntityTypeConfiguration<ApplicationRole> entityTypeConfiguration1 = 
                  modelBuilder.Entity<ApplicationRole>().ToTable("AspNetRoles");
              entityTypeConfiguration1.Property((ApplicationRole r) => r.Name).IsRequired();
          }

           

          Next, we need to explicitly add a Groups property on ApplicationDbContext. Once again, this needs to be a virtual property, but in this case the return type is ICollection<Group>:

          Add the Groups Property to ApplicationDbcontext:
          publicclass ApplicationDbContext : IdentityDbContext<ApplicationUser>
          {
          // Add an instance IDbSet using the 'new' keyword:
          newpublicvirtual IDbSet<ApplicationRole> Roles { get; set; }
          // ADD THIS:
          publicvirtual IDbSet<Group> Groups { get; set; }
          public ApplicationDbContext()
                  : base("DefaultConnection")
              {
              }
          protectedoverridevoid OnModelCreating(DbModelBuilder modelBuilder)
              {
          // Code we just added above is here . .  .
              }
          // Etc . . .
          }

           

          Add Group Management Items to Identity Manager

          Now, let's add some code to help us manage the various functionality we need related to Groups, and management of users and Roles ("permissions") related to Groups. Look over the methods below carefully to understand just what is going on most of the time, as several of the actions one might take upon a group have potential consequences across the security spectrum.

          For example, when we delete a group, we need to also:

          • Remove all the users from the group. Remember, there is a foreign key relationship here with an intermediate or "relations table" - related records need to be removed first, or we will generally get a key constraint error.
          • Remove all the roles from that group. Remember, there is a foreign key relationship here with an intermediate or "relations table" - related records need to be removed first, or we will generally get a key constraint error.
          • Remove the roles from each user, except when that user has the same role resulting from membership in another group (this was a pain to think through!).

          Likewise, when we add a Role ("Permission") to a group, we need to update all of the users in that group to reflect the added permission.

          Add the following methods to the bottom of the existing IdentityManager class:

          Add Group Methods to Identity Manager Class:
          publicvoid CreateGroup(string groupName)
          {
          if (this.GroupNameExists(groupName))
              {
          thrownew System.Exception("A group by that name already exists in the database. Please choose another name.");
              }
              var newGroup = new Group(groupName);
              _db.Groups.Add(newGroup);
              _db.SaveChanges();
          }
          publicbool GroupNameExists(string groupName)
          {
              var g = _db.Groups.Where(gr => gr.Name == groupName);
          if (g.Count() > 0)
              {
          returntrue;
              }
          returnfalse;
          }
          publicvoid ClearUserGroups(string userId)
          {
          this.ClearUserRoles(userId);
              var user = _db.Users.Find(userId);
              user.Groups.Clear();
              _db.SaveChanges();
          }
          publicvoid AddUserToGroup(string userId, int GroupId)
          {
              var group = _db.Groups.Find(GroupId);
              var user = _db.Users.Find(userId);
              var userGroup = new ApplicationUserGroup()
              {
                  Group = group,
                  GroupId = group.Id,
                  User = user,
                  UserId = user.Id
              };
          foreach (var role in group.Roles)
              {
                  _userManager.AddToRole(userId, role.Role.Name);
              }
              user.Groups.Add(userGroup);
              _db.SaveChanges();
          }
          publicvoid ClearGroupRoles(int groupId)
          {
              var group = _db.Groups.Find(groupId);
              var groupUsers = _db.Users.Where(u => u.Groups.Any(g => g.GroupId == group.Id));
          foreach (var role in group.Roles)
              {
                  var currentRoleId = role.RoleId;
          foreach (var user in groupUsers)
                  {
          // Is the user a member of any other groups with this role?
                      var groupsWithRole = user.Groups
                          .Where(g => g.Group.Roles
                              .Any(r => r.RoleId == currentRoleId)).Count();
          // This will be 1 if the current group is the only one:
          if (groupsWithRole == 1)
                      {
          this.RemoveFromRole(user.Id, role.Role.Name);
                      }
                  }
              }
              group.Roles.Clear();
              _db.SaveChanges();
          }
          publicvoid AddRoleToGroup(int groupId, string roleName)
          {
              var group = _db.Groups.Find(groupId);
              var role = _db.Roles.First(r => r.Name == roleName);
              var newgroupRole = new ApplicationRoleGroup()
              {
                  GroupId = group.Id,
                  Group = group,
                  RoleId = role.Id,
                  Role = (ApplicationRole)role
              };
              group.Roles.Add(newgroupRole);
              _db.SaveChanges();
          // Add all of the users in this group to the new role:
              var groupUsers = _db.Users.Where(u => u.Groups.Any(g => g.GroupId == group.Id));
          foreach (var user in groupUsers)
              {
          if(!(_userManager.IsInRole(user.Id, roleName)))
                  {
          this.AddUserToRole(user.Id, role.Name);
                  }
              }
          }
          publicvoid DeleteGroup(int groupId)
          {
              var group = _db.Groups.Find(groupId);
          // Clear the roles from the group:
          this.ClearGroupRoles(groupId);
              _db.Groups.Remove(group);
              _db.SaveChanges();
          }

           

          We now have the core code needed to manage the relationships between Users, Groups, and Roles ("Permissions") in the back end. Now we need to set up our Migrations Configuration file to properly seed our database when we run EF Migrations.

          Update the Migrations Configuration File to Seed the Database

          Most of the basic model stuff is now in place such that we can run EF Migrations and build out our modified database. Before we do that, though, we want to update our Migrations Configuration class so that we seed our database with the minimal required data to function. Remember, our site is closed to "public" registration. Therefore, at the very least we need to seed it with an initial admin-level user, just like before.

          What is NOT like before is that we have changed the manner in which roles are assigned and managed. Going forward, we need to seed our initial user, along with one or more initial Groups, and seed at least one of those groups with sufficient admin permissions that our initial user can take it from there.

          There are many ways this code could be written. Further, depending upon your application requirements, how the database is seeded may become an extensive exercise in planning (remember that bit about how a more complex authorization model requires more and more up-front planning?).

          Here, we are going to update our Configuration class with a few new methods. We will add an initial user, a handful of potentially useful Groups, and some roles relevant to managing security and authorization.

          Updated Migrations Configuration File:
          internalsealedclass Configuration 
              : DbMigrationsConfiguration<ApplicationDbContext>
          {
              IdentityManager _idManager = new IdentityManager();
              ApplicationDbContext _db = new ApplicationDbContext();
          public Configuration()
              {
                  AutomaticMigrationsEnabled = true;
              }
          protectedoverridevoid Seed(ApplicationDbContext context)
              {
          this.AddGroups();
          this.AddRoles();
          this.AddUsers();
          this.AddRolesToGroups();
          this.AddUsersToGroups();
              }
          string[] _initialGroupNames = 
          newstring[] { "SuperAdmins", "GroupAdmins", "UserAdmins", "Users" };
          publicvoid AddGroups()
              {
          foreach (var groupName in _initialGroupNames)
                  {
                      _idManager.CreateGroup(groupName);
                  }
              }
          void AddRoles()
              {
          // Some example initial roles. These COULD BE much more granular:
                  _idManager.CreateRole("Admin", "Global Access");
                  _idManager.CreateRole("CanEditUser", "Add, modify, and delete Users");
                  _idManager.CreateRole("CanEditGroup", "Add, modify, and delete Groups");
                  _idManager.CreateRole("CanEditRole", "Add, modify, and delete roles");
                  _idManager.CreateRole("User", "Restricted to business domain activity");
              }
          string[] _superAdminRoleNames = 
          newstring[] { "Admin", "CanEditUser", "CanEditGroup", "CanEditRole", "User" };
          string[] _groupAdminRoleNames =
          newstring[] { "CanEditUser", "CanEditGroup", "User" };
          string[] _userAdminRoleNames =
          newstring[] { "CanEditUser", "User" };
          string[] _userRoleNames =
          newstring[] { "User" };
          void AddRolesToGroups()
              {
          // Add the Super-Admin Roles to the Super-Admin Group:
                  var allGroups = _db.Groups;
                  var superAdmins = allGroups.First(g => g.Name == "SuperAdmins");
          foreach (string name in _superAdminRoleNames)
                  {
                      _idManager.AddRoleToGroup(superAdmins.Id, name);
                  }
          // Add the Group-Admin Roles to the Group-Admin Group:
                  var groupAdmins = _db.Groups.First(g => g.Name == "GroupAdmins");
          foreach (string name in _groupAdminRoleNames)
                  {
                      _idManager.AddRoleToGroup(groupAdmins.Id, name);
                  }
          // Add the User-Admin Roles to the User-Admin Group:
                  var userAdmins = _db.Groups.First(g => g.Name == "UserAdmins");
          foreach (string name in _userAdminRoleNames)
                  {
                      _idManager.AddRoleToGroup(userAdmins.Id, name);
                  }
          // Add the User Roles to the Users Group:
                  var users = _db.Groups.First(g => g.Name == "Users");
          foreach (string name in _userRoleNames)
                  {
                      _idManager.AddRoleToGroup(users.Id, name);
                  }
              }
          // Change these to your own:
          string _initialUserName = "jatten";
          string _InitialUserFirstName = "John";
          string _initialUserLastName = "Atten";
          string _initialUserEmail = "jatten@typecastexception.com";
          void AddUsers()
              {
                  var newUser = new ApplicationUser()
                  {
                      UserName = _initialUserName,
                      FirstName = _InitialUserFirstName,
                      LastName = _initialUserLastName,
                      Email = _initialUserEmail
                  };
          // Be careful here - you  will need to use a password which will 
          // be valid under the password rules for the application, 
          // or the process will abort:
                  _idManager.CreateUser(newUser, "Password1");
              }
          // Configure the initial Super-Admin user:
          void AddUsersToGroups()
              {
                  var user = _db.Users.First(u => u.UserName == _initialUserName);
                  var allGroups = _db.Groups;
          foreach (var group in allGroups)
                  {
                      _idManager.AddUserToGroup(user.Id, group.Id);
                  }
              }
          }

           

          As you can see in the above, I have (rather arbitrarily) decided to set up some initial groups and roles related to the Users/Groups/Roles domain. If we already knew the domain structure of the rest of our application, we might want to include additional roles ("Permissions") as part of our Configuration, since roles need to be hard-coded into our controllers using the [Authorize] attribute. The earlier we can determine the role structure for our application security model, the better. You will want to strike a balance between granularity and manageability here, though.

          For the moment, we have a sufficient starting point, and we are ready to run EF Migrations and see if our database is built successfully.

          Run Migrations and Build Out the Database

          As mentioned previously, as I did this, I deleted the previous Migration files, but left the Migrations folder intact, with the (now modified Configuration.cs file). Therefore, in order to perform the migration, I simply type the following into the Package Manager Console:

          Add New Migration:
          PM> Add-Migration init

           

          This scaffolds up a new migration. Next:

          Build Out the Database:
          PM> Update-Database

           

          If everything went well, we should be able to open our database in the Visual Studio Server Explorer and see how we did. You should see something like this:

          The Database in VS Server Explorer:

          vs-server-explorer-database-view

          Looks like everything went ok!

          Next: Controllers, ViewModels, and Views

          This article became long enough that I decided to break it into two parts. In this post, we figured out how to model our Users, Groups, and Roles ("Permissions") in our application, and by extension, in our database via EF Code-First and Migrations.

          Next, we will start pulling all this together into the business end of our application

          Next:  Part II - Controllers, ViewModels, and Views --->

           

          Additional Resources and Items of Interest

          ASP.NET MVC 5 Identity: Implementing Group-Based Permissions Management Part II

          $
          0
          0

          locked-awayThis is the second part of a two-part series in which we figure out how to implement a basic Group-based permissions management system using the ASP.NET MVC 5 Identity system. In this series, we are building upon previous concepts we used in extending the IdentityUser class and implementing Role-Based application security, and also in extending and customizing the IdentityRole class.

          In this series, we hope to overcome some of the limitations of the simple "Users and Roles" security model, instead assigning Roles ("permissions") to Groups, and then assigning one or more groups to each user.

          Image by Kool | Some Rights Reserved

          In the first installment, we figured out how to model our core domain objects for the purpose of extending the ASP.NET Identity system into a basic Group-Based Permissions management mode. We decided that Groups will be assigned various combinations of permissions, and Users are assigned to one or more groups. What we are referring to here as "permissions" are actually the familiar "Role" provided by the identity system, upon which the MVC authorization system depends for user authentication and application access authorization.

          <--- Review Part I: Extending the Model

           

          Up to this point, we have extended our domain model by adding a Group class, implemented many-to-many relationships between Users and Groups, as well as between Groups and Roles ("Permissions"). We created

          Building the Example Application - Controllers, Views, and ViewModels

          Picking up where we left off, we now need to add the functional components of our example application. Obviously, we need some controllers and Views, but before we can build those, we are going to add some ViewModels which will be consumed by the various Controllers, and passed to the Views.

          Adding Group View Models

          We are going to need a few new ViewModels to complete our implementation of Groups. Also, we no longer need the SelectUserRolesViewModel. We will now be assigning Users to Groups, instead of directly to Roles, so we can delete the code for that. For the sake of simplicity, we will go ahead and add all of our new ViewModels to the AccountViewModels.cs file, and then I will explain what we are doing with each on as we go.

          First, open the AccountViewModels.cs file, find the code for SelectUserRolesViewModel, and delete it.

          Next, add the following new ViewModels to the end of the AccountViewModels.cs file:

          Add New Required ViewModels:
          // Wrapper for SelectGroupEditorViewModel to select user group membership:
          publicclass SelectUserGroupsViewModel
          {
          publicstring UserName { get; set; }
          publicstring FirstName { get; set; }
          publicstring LastName { get; set; }
          public List<SelectGroupEditorViewModel> Groups { get; set; }
          public SelectUserGroupsViewModel()
              {
          this.Groups = new List<SelectGroupEditorViewModel>();
              }
          public SelectUserGroupsViewModel(ApplicationUser user)
                  : this()
              {
          this.UserName = user.UserName;
          this.FirstName = user.FirstName;
          this.LastName = user.LastName;
                  var Db = new ApplicationDbContext();
          // Add all available groups to the public list:
                  var allGroups = Db.Groups;
          foreach (var role in allGroups)
                  {
          // An EditorViewModel will be used by Editor Template:
                      var rvm = new SelectGroupEditorViewModel(role);
          this.Groups.Add(rvm);
                  }
          // Set the Selected property to true where user is already a member:
          foreach (var group in user.Groups)
                  {
                      var checkUserRole =
          this.Groups.Find(r => r.GroupName == group.Group.Name);
                      checkUserRole.Selected = true;
                  }
              }
          }
          // Used to display a single group with a checkbox, within a list structure:
          publicclass SelectGroupEditorViewModel
          {
          public SelectGroupEditorViewModel() { }
          public SelectGroupEditorViewModel(Group group)
              {
          this.GroupName = group.Name;
          this.GroupId = group.Id;
              }
          publicbool Selected { get; set; }
              [Required]
          publicint GroupId { get; set; }
          publicstring GroupName { get; set; }
          }
          publicclass SelectGroupRolesViewModel
          {
          public SelectGroupRolesViewModel()
              {
          this.Roles = new List<SelectRoleEditorViewModel>();
              }
          // Enable initialization with an instance of ApplicationUser:
          public SelectGroupRolesViewModel(Group group)
                  : this()
              {
          this.GroupId = group.Id;
          this.GroupName = group.Name;
                  var Db = new ApplicationDbContext();
          // Add all available roles to the list of EditorViewModels:
                  var allRoles = Db.Roles;
          foreach (var role in allRoles)
                  {
          // An EditorViewModel will be used by Editor Template:
                      var rvm = new SelectRoleEditorViewModel(role);
          this.Roles.Add(rvm);
                  }
          // Set the Selected property to true for those roles for 
          // which the current user is a member:
          foreach (var groupRole in group.Roles)
                  {
                      var checkGroupRole =
          this.Roles.Find(r => r.RoleName == groupRole.Role.Name);
                      checkGroupRole.Selected = true;
                  }
              }
          publicint GroupId { get; set; }
          publicstring GroupName { get; set; }
          public List<SelectRoleEditorViewModel> Roles { get; set; }
          }
          publicclass UserPermissionsViewModel
          {
          public UserPermissionsViewModel()
              {
          this.Roles = new List<RoleViewModel>();
              }
          // Enable initialization with an instance of ApplicationUser:
          public UserPermissionsViewModel(ApplicationUser user)
                  : this()
              {
          this.UserName = user.UserName;
          this.FirstName = user.FirstName;
          this.LastName = user.LastName;
          foreach (var role in user.Roles)
                  {
                      var appRole = (ApplicationRole)role.Role;
                      var pvm = new RoleViewModel(appRole);
          this.Roles.Add(pvm);
                  }
              }
          publicstring UserName { get; set; }
          publicstring FirstName { get; set; }
          publicstring LastName { get; set; }
          public List<RoleViewModel> Roles { get; set; }
          }

           

          Adding a Groups Controller

          Before we can do much more with our application, we need to add a Groups controller and associated Views. We already have a Roles Controller from our previous article, so let's add one for Groups now. We begin with a pretty standard CRUD-type controller as might be generated by Visual Studio:

          The basic Groups Controller:
          publicclass GroupsController : Controller
          {
          private ApplicationDbContext db = new ApplicationDbContext();
              [Authorize(Roles = "Admin, CanEditGroup, CanEditUser")]
          public ActionResult Index()
              {
          return View(db.Groups.ToList());
              }
              [Authorize(Roles = "Admin, CanEditGroup, CanEditUser")]
          public ActionResult Details(int? id)
              {
          if (id == null)
                  {
          returnnew HttpStatusCodeResult(HttpStatusCode.BadRequest);
                  }
                  Group group = db.Groups.Find(id);
          if (group == null)
                  {
          return HttpNotFound();
                  }
          return View(group);
              }
              [Authorize(Roles = "Admin, CanEditGroup")]
          public ActionResult Create()
              {
          return View();
              }
              [Authorize(Roles = "Admin, CanEditGroup")]
              [HttpPost]
              [ValidateAntiForgeryToken]
          public ActionResult Create([Bind(Include="Name")] Group group)
              {
          if (ModelState.IsValid)
                  {
                      db.Groups.Add(group);
                      db.SaveChanges();
          return RedirectToAction("Index");
                  }
          return View(group);
              }
              [Authorize(Roles = "Admin, CanEditGroup")]
          public ActionResult Edit(int? id)
              {
          if (id == null)
                  {
          returnnew HttpStatusCodeResult(HttpStatusCode.BadRequest);
                  }
                  Group group = db.Groups.Find(id);
          if (group == null)
                  {
          return HttpNotFound();
                  }
          return View(group);
              }
              [Authorize(Roles = "Admin, CanEditGroup")]
              [HttpPost]
              [ValidateAntiForgeryToken]
          public ActionResult Edit([Bind(Include="Name")] Group group)
              {
          if (ModelState.IsValid)
                  {
                      db.Entry(group).State = EntityState.Modified;
                      db.SaveChanges();
          return RedirectToAction("Index");
                  }
          return View(group);
              }
              [Authorize(Roles = "Admin, CanEditGroup")]
          public ActionResult Delete(int? id)
              {
          if (id == null)
                  {
          returnnew HttpStatusCodeResult(HttpStatusCode.BadRequest);
                  }
                  Group group = db.Groups.Find(id);
          if (group == null)
                  {
          return HttpNotFound();
                  }
          return View(group);
              }
              [Authorize(Roles = "Admin, CanEditGroup")]
              [HttpPost, ActionName("Delete")]
              [ValidateAntiForgeryToken]
          public ActionResult DeleteConfirmed(int id)
              {
                  Group group = db.Groups.Find(id);
                  var idManager = new IdentityManager();
                  idManager.DeleteGroup(id);
          return RedirectToAction("Index");
              }
              [Authorize(Roles = "Admin, CanEditGroup")]
          public ActionResult GroupRoles(int id)
              {
                  var group = db.Groups.Find(id);
                  var model = new SelectGroupRolesViewModel(group);
          return View(model);
              }
              [HttpPost]
              [Authorize(Roles = "Admin, CanEditGroup")]
              [ValidateAntiForgeryToken]
          public ActionResult GroupRoles(SelectGroupRolesViewModel model)
              {
          if (ModelState.IsValid)
                  {
                      var idManager = new IdentityManager();
                      var Db = new ApplicationDbContext();
                      var group = Db.Groups.Find(model.GroupId);
                      idManager.ClearGroupRoles(model.GroupId);
          // Add each selected role to this group:
          foreach (var role in model.Roles)
                      {
          if (role.Selected)
                          {
                              idManager.AddRoleToGroup(group.Id, role.RoleName);
                          }
                      }
          return RedirectToAction("index");
                  }
          return View();
              }
          protectedoverridevoid Dispose(bool disposing)
              {
          if (disposing)
                  {
                      db.Dispose();
                  }
          base.Dispose(disposing);
              }
          }

           

          By now, most of the code above should look fairly familiar. We have the basic Create/Edit/Delete and Index methods, and then one odd method at the end of the class, GroupRoles. Actually, this should look sort of familiar as well. This is a simple adaptation of the code we used in the previous project to select Roles for individual Users. Here, we are doing the same thing for specific Groups instead.

          Add Views for the Groups Controller

          The following are the Views we need for the Groups Controller. These are also pretty standard fare, except for the GroupRoles View. We'll include them all here for completeness, though.

          Of greatest interest is the GroupRoles View, so we will start there.

          The GroupRoles View

          This View is where we will assign one or more Roles to a specific Group. We want to display the general information for the current group selected, and then display a list of all the available Roles, with checkboxes to indicate selected status. For our presentation layer, we will describe Roles as "Permissions" so that the concept is more clear to the user: Users are members of groups, and groups have sets of permissions.

          Here, we once again employ an EditorTemplate (our SelectRoleEditorTemplate from the previous version of this project) in order to render an HTML Table with checkboxes to indicate selection status for each row item. This View is nearly identical to the View we used in the previous version of this project for for the UserRoles View (in fact, I simply made a few quick changes to that one, and renamed it)..

          The GroupRoles View:
          @model AspNetGroupBasedPermissions.Models.SelectGroupRolesViewModel
          @{ ViewBag.Title = "Group Role Permissions"; }
          <h2>Permissions for Group @Html.DisplayFor(model => model.GroupName)</h2>
          <hr />
          @using (Html.BeginForm("GroupRoles", "Groups", FormMethod.Post, new { encType = "multipart/form-data", name = "myform" }))
          {
              @Html.AntiForgeryToken()
          <div class="form-horizontal">
                  @Html.ValidationSummary(true)
          <div class="form-group">
          <div class="col-md-10">
                          @Html.HiddenFor(model => model.GroupName)
          </div>
          </div>
          <div class="form-group">
          <div class="col-md-10">
                          @Html.HiddenFor(model => model.GroupId)
          </div>
          </div>
          <h4>Select Role Permissions for @Html.DisplayFor(model => model.GroupName)</h4>
          <br />
          <hr />
          <table>
          <tr>
          <th>
                              Select
          </th>
          <th>
                              Permissions
          </th>
          </tr>
                      @Html.EditorFor(model => model.Roles)
          </table>
          <br />
          <hr />
          <div class="form-group">
          <div class="col-md-offset-2 col-md-10">
          <input type="submit" value="Save" class="btn btn-default" />
          </div>
          </div>
          </div>
          }
          <div>
              @Html.ActionLink("Back to List", "Index")
          </div>

           

          In the above, the line @html.EditorFor(model => model.Roles) causes the MVC framework to dig out our (Carefully named!!) SelectRoleEditorViewModel from the Views/Shared/EditorTempates/ directory, and uses that to render each Role item as a table row.

          If you have been following this "series" of articles, this should be familiar territory by now.

          From here, the rest of these views are rather standard fare.

          The Index Group View

          Code for the Index Group View:
          @model IEnumerable<AspNetGroupBasedPermissions.Models.Group>
          @{ ViewBag.Title = "Index"; }
          <h2>Groups</h2>
          <p>
              @Html.ActionLink("Create New", "Create")
          </p>
          <table class="table">
          <tr>
          <th>
                      @Html.DisplayNameFor(model => model.Name)
          </th>
          <th></th>
          </tr>
          @foreach (var item in Model) 
          {
          <tr>
          <td>
                      @Html.DisplayFor(modelItem => item.Name)
          </td>
          <td>
                      @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
                      @Html.ActionLink("Permissions", "GroupRoles", new { id=item.Id }) |
                      @Html.ActionLink("Delete", "Delete", new { id=item.Id })
          </td>
          </tr>
          }
          </table>

           

          Note above, we have added three ActionLinks at the end of each row - "Edit", "Permissions", and "Delete."

          These will link us to the appropriate methods on the RolesController. Of specific interest is the "Permissions" link, which will direct us to our GroupRoles method, and allow us to assign one or more Roles ("Permissions") to each group. This, so to speak, is the business end of our authorization management.

          The Create Group View

          The Create Group View:
          @model AspNetGroupBasedPermissions.Models.Group
          @{ ViewBag.Title = "Create Groups"; }
          <h2>Create a new Group</h2>
          @using (Html.BeginForm()) 
          {
              @Html.AntiForgeryToken()
          <div class="form-horizontal">
          <h4>Group</h4>
          <hr />
                  @Html.ValidationSummary(true)
          <div class="form-group">
                      @Html.LabelFor(model => model.Name, new { @class = "control-label col-md-2" })
          <div class="col-md-10">
                          @Html.EditorFor(model => model.Name)
                          @Html.ValidationMessageFor(model => model.Name)
          </div>
          </div>
          <div class="form-group">
          <div class="col-md-offset-2 col-md-10">
          <input type="submit" value="Create" class="btn btn-default" />
          </div>
          </div>
          </div>
          }
          <div>
              @Html.ActionLink("Back to List", "Index")
          </div>
          @section Scripts { @Scripts.Render("~/bundles/jqueryval") }

           

          The Edit Group View

          Code for the Edit Group View:
          @model AspNetGroupBasedPermissions.Models.Group
          @{ ViewBag.Title = "Edit"; }
          <h2>Edit</h2>
          @using (Html.BeginForm())
          {
              @Html.AntiForgeryToken()
          <div class="form-horizontal">
          <h4>Group</h4>
          <hr />
                  @Html.ValidationSummary(true)
                  @Html.HiddenFor(model => model.Id)
          <div class="form-group">
                      @Html.LabelFor(model => model.Name, new { @class = "control-label col-md-2" })
          <div class="col-md-10">
                          @Html.EditorFor(model => model.Name)
                          @Html.ValidationMessageFor(model => model.Name)
          </div>
          </div>
          <div class="form-group">
          <div class="col-md-offset-2 col-md-10">
          <input type="submit" value="Save" class="btn btn-default" />
          </div>
          </div>
          </div>
          }
          <div>
              @Html.ActionLink("Back to List", "Index")
          </div>
          @section Scripts { @Scripts.Render("~/bundles/jqueryval") }

           

          The Delete Group View

          Code for the Delete Group View:
          @model AspNetGroupBasedPermissions.Models.Group
          @{ ViewBag.Title = "Delete"; }
          <h2>Delete</h2>
          <h3>Are you sure you want to delete this?</h3>
          <div>
          <h4>Group</h4>
          <hr />
          <dl class="dl-horizontal">
          <dt>
                      @Html.DisplayNameFor(model => model.Name)
          </dt>
          <dd>
                      @Html.DisplayFor(model => model.Name)
          </dd>
          </dl>
              @using (Html.BeginForm()) {
                  @Html.AntiForgeryToken()
          <div class="form-actions no-color">
          <input type="submit" value="Delete" class="btn btn-default" /> |
                      @Html.ActionLink("Back to List", "Index")
          </div>
              }
          </div>

           

          Update AccountController to Assign Users to Groups

          Now that we have our Groups controller and Views in place, we need to update the code on our original AccountController. Previously, we have created a UserRoles method on AccountController, by which we assigned one or more Roles to a specific user. Now, instead, we are going to be assigning one or more Groups to specific User.

          Open the AccountController file, and delete the UserRoles method, replacing it with the following code for UserGroups:

          The UserGroups Method for AccountController:
          [Authorize(Roles = "Admin, CanEditUser")]
          public ActionResult UserGroups(string id)
          {
              var user = _db.Users.First(u => u.UserName == id);
              var model = new SelectUserGroupsViewModel(user);
          return View(model);
          }
          [HttpPost]
          [Authorize(Roles = "Admin, CanEditUser")]
          [ValidateAntiForgeryToken]
          public ActionResult UserGroups(SelectUserGroupsViewModel model)
          {
          if (ModelState.IsValid)
              {
                  var idManager = new IdentityManager();
                  var user = _db.Users.First(u => u.UserName == model.UserName);
                  idManager.ClearUserGroups(user.Id);
          foreach (var group in model.Groups)
                  {
          if (group.Selected)
                      {
                          idManager.AddUserToGroup(user.Id, group.GroupId);
                      }
                  }
          return RedirectToAction("index");
              }
          return View();
          }

          Notice in the above, when we the HTTP Post is returned from the View, we need to clear all the User Group assignments, and then individually add the user to each of the groups selected in the ViewModel.

          Replace the UserRoles View with a UserGroups View

          With our new UserGroups method in place on AccountController, we need to replace the former UserRoles View with a very similar UserGroups View. As previously, we will display the basic User data for a specific, user, along with a list of available Groups to which the the User might be assigned. Once again, using a table with checkboxes, we can assign the user to one or more Groups.

          As with the now-deprecated UserRoles View, and also the newly added GroupRoles View, we need a special Editor Template ViewModel and a correspondingly-named Editor Template View to represent each Group in the table. We have already added a SelectGroupEditorViewModel to the AccountViewModels.cs file. Now we need to add the corresponding Editor Template View.

          Add the following View to the Views/Shared/EditorTemplates/ directory.Be careful to name it SelectGroupEditorViewModel so that it exactly matches the name of the ViewModel it represents:

          The SelectGroupEditorViewModel View:
          @model AspNetGroupBasedPermissions.Models.SelectGroupEditorViewModel
          @Html.HiddenFor(model => model.GroupId)
          <tr>
          <td style="text-align:center">
                  @Html.CheckBoxFor(model => model.Selected)
          </td>
          <td style="padding-right:20px">
                  @Html.DisplayFor(model => model.GroupName)
          </td>
          </tr>

           

          Now, with the in place, delete the old UserRoles View from the Views/Account/ directory, and add a new View named UserGroups as follows:

          The UserGoups View:
          @model AspNetGroupBasedPermissions.Models.SelectUserGroupsViewModel
          @{ ViewBag.Title = "User Groups"; }
          <h2>Groups for user @Html.DisplayFor(model => model.UserName)</h2>
          <hr />
          @using (Html.BeginForm("UserGroups", "Account", FormMethod.Post, new { encType = "multipart/form-data", name = "myform" }))
          {
              @Html.AntiForgeryToken()
          <div class="form-horizontal">
                  @Html.ValidationSummary(true)
          <div class="form-group">
          <div class="col-md-10">
                          @Html.HiddenFor(model => model.UserName)
          </div>
          </div>
          <h4>Select Group Assignments</h4>
          <br />
          <hr />
          <table>
          <tr>
          <th>
                              Select
          </th>
          <th>
                              Group
          </th>
          </tr>
                      @Html.EditorFor(model => model.Groups)
          </table>
          <br />
          <hr />
          <div class="form-group">
          <div class="col-md-offset-2 col-md-10">
          <input type="submit" value="Save" class="btn btn-default" />
          </div>
          </div>
          </div>
          }
          <div>
              @Html.ActionLink("Back to List", "Index")
          </div>

           

          Update the Action Links on the Account/Index View

          We need to make a minor update to the Account Index View. Currently, the links next to each User in the table indicate "Roles" and point to the (now non-existent) UserRoles method. Instead, we will display the text "Groups" and point the link at the newly added UserGroups method. The modified code should look like the following:

          Update the ActionLinks for the Table Items in the Account/Index View:
          @model IEnumerable<AspNetGroupBasedPermissions.Models.EditUserViewModel>
          @{ ViewBag.Title = "Index"; }
          <h2>Index</h2>
          <p>
              @Html.ActionLink("Create New", "Register") 
          </p>
          <table class="table">
          <tr>
          <th>
                      @Html.DisplayNameFor(model => model.UserName)
          </th>
          <th>
                      @Html.DisplayNameFor(model => model.FirstName)
          </th>
          <th>
                      @Html.DisplayNameFor(model => model.LastName)
          </th>
          <th>
                      @Html.DisplayNameFor(model => model.Email)
          </th>
          <th></th>
          </tr>
          @foreach (var item in Model) {
          <tr>
          <td>
                      @Html.DisplayFor(modelItem => item.UserName)
          </td>
          <td>
                      @Html.DisplayFor(modelItem => item.FirstName)
          </td>
          <td>
                      @Html.DisplayFor(modelItem => item.LastName)
          </td>
          <td>
                      @Html.DisplayFor(modelItem => item.Email)
          </td>
          <td>
                      @Html.ActionLink("Edit", "Edit", new { id = item.UserName }) |
                      @Html.ActionLink("Groups", "UserGroups", new { id = item.UserName }) |
                      @Html.ActionLink("Delete", "Delete", new { id = item.UserName })
          </td>
          </tr>
          }
          </table>

           

          Update Navigation Links on _Layout.cshtml

          Now we just need to make sure we can access all of the new functionality we just built into our application. In the middle of the code on the _Layout.cshtml file, we need to update the Navigation links to match this:

          Updated Navigation Links on _Layout.cshtml
          <div class="navbar-collapse collapse">
          <ul class="nav navbar-nav">
          <li>@Html.ActionLink("Home", "Index", "Home")</li>
          <li>@Html.ActionLink("About", "About", "Home")</li>
          <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
          <li>@Html.ActionLink("Users", "Index", "Account")</li>
          <li>@Html.ActionLink("Groups", "Index", "Groups")</li>
          <li>@Html.ActionLink("Permissions", "Index", "Roles")</li>
          </ul>
              @Html.Partial("_LoginPartial")
          </div>

           

          Setting up Initial Authorization Permissions

          What is not obvious in the code here (unless you cloned the completed project) is that we are implementing our new Users/Groups/Permissions model upon the controllers we just created. Given the example Roles ("Permissions" I included in the Migrations Configuration file (what we are "Seeding" the database with), I set up the initial method-level [Authorize] attributes using the following permissions scheme. If it is not already, add the following [Authorize] Attributes to the appropriate method on each controller. Replace any that exist from the previous project.

          Account Controller [Authorize] Roles:

          Action Method

          Roles Allowed
          Login[AllowAnonymous]
          Register[Authorize(Roles = "Admin, CanEditUser")]
          Manage[Authorize(Roles = "Admin, CanEditUser, User")]
          Index[Authorize(Roles = "Admin, CanEditGroup, CanEditUser")]
          Edit[Authorize(Roles = "Admin, CanEditUser")]
          Delete[Authorize(Roles = "Admin, CanEditUser")]
          UserGroups[Authorize(Roles = "Admin, CanEditUser")]

           

          Groups Controller [Authorize] Roles:

          Action Method

          Roles Allowed
          Index[Authorize(Roles = "Admin, CanEditGroup, CanEditUser")]
          Details[Authorize(Roles = "Admin, CanEditGroup, CanEditUser")]
          Create[Authorize(Roles = "Admin, CanEditGroup")]
          Edit[Authorize(Roles = "Admin, CanEditGroup")]
          Delete[Authorize(Roles = "Admin, CanEditGroup")]
          GroupRoles[Authorize(Roles = "Admin, CanEditGroup")]

           

          Roles Controller [Authorize] Roles:

          Action Method

          Roles Allowed
          Index[Authorize(Roles = "Admin, CanEditGroup, CanEditUser")]
          Create[Authorize(Roles = "Admin")]
          Edit[Authorize(Roles = "Admin")]
          Delete[Authorize(Roles = "Admin")]

           

          As we can see, I have done my best (within space and the practical constraints of this already lengthy example project) to structure a tiered authorization scheme, modestly following a sort of "Principle of Least Privilege." In a production application, we can assume you would have additional business domains, and would need to think through the Role permission assignments with care.

          Running the Application

          If we start our application, we should be greeted with the standard Login screen. Once logged in, if we navigate to the Groups Link, we should be greeted with a list of the Groups we seeded our database with:

          The Groups Screen:

          application-groups_thumb3

          If we click on the "Permissions" link, we find the roles, or "permissions" currently assigned to a particular group:

          Permissions Screen:

          application-group-roles_thumb3

          If we navigate to the Users screen, we see what we might expect - a list of users, with the option to drill down and see which Groups a specific User belongs to:

          The Users Screen:

          application-users_thumb2

          What might be Handy, though, is an additional link for each listed user whereby we can see what permissions they have as a result of all the groups in which they participate.

          Adding a View For User Effective Permissions

          Fortunately, we can do just that. First, we need to add one more link to the Accounts/Index View. Near the bottom, where we set up the links next to each row of table data, add the link as below for "Effective Permissions:

          Add Effective Permissions Link to Account/Index View:
          <td>
              @Html.ActionLink("Edit", "Edit", new { id = item.UserName }) |
              @Html.ActionLink("Groups", "UserGroups", new { id = item.UserName }) |
              @Html.ActionLink("Effective Permissions", "UserPermissions", new { id = item.UserName }) ||| 
              @Html.ActionLink("Delete", "Delete", new { id = item.UserName })
          </td>

           

          We have already added the UserPermissionsViewModel to AccountViewModels.cs, so now we just need to add a UserPermissions view to the Views/Account directory:

          The UserPermissions View:
          @model AspNetGroupBasedPermissions.Models.UserPermissionsViewModel
          @{ ViewBag.Title = "UserPermissions"; }
          <h2>Effective Role Permissions for user: @Html.DisplayFor(model => model.UserName)</h2>
          <hr />
          <table class="table">
          <tr>
          <th>
                      Role Permission
          </th>
          <th>
                     Description
          </th>
          <th></th>
          </tr>
              @foreach (var item in Model.Roles)
              {
          <tr>
          <td>
                          @Html.DisplayFor(modelItem => item.RoleName)
          </td>
          <td>
                          @Html.DisplayFor(modelItem => item.Description)
          </td>
          </tr>
              }
          </table>
          <div>
              @Html.ActionLink("Back to List", "Index")
          </div>

           

          Now, if we run our application, we can drill down and see all of the permissions afforded a given user as a result of all the groups in which that user participates:

          Navigate to Users to Find Effective Permissions Link:

          users-before-effective-permission-se[1]

           

          View Effective Permissions for the Selected User across All Ggroups:

          users-after-effective-permission-sel[2]

          Some Thoughts About Authorization Management and Your Website

          The ASP.NET Identity system affords us an easy to use abstraction over a complicated topic, and one which is at the forefront of today's news ("Ripped from today's headlines" so to speak). The ASP.NET Identity system represents one option for securing your site and managing authentication, but it is not the only way. The ASP.NET team presents several options for site security, and in fact Visual Studio gives you several choices as part of setting up your MVC project.

          The Identity system appears to be a good choice for public-facing websites, integration with social media providers, and sites with simpler permissions management needs. Other options for managing site security include Active directory integration, and Windows Authentication for intranet-based services.

          Of these options, the Identity system is the easiest to implement, and is built-in to the project templates included with ASP.NET and MVC.

          As mentioned repeatedly throughout this article, the more finely-grained your security system can be, the more control you have. However, that control comes at the price of complexity. Not just in terms of the code, but also in terms of managing the various Users, Groups, and permission sets you create.

          I guarantee you don't want to be sprinkling new roles willy-nilly throughout your application code, and then trying to manage them later. Plot it out ahead of time, with your business domains firmly in mind. Strike a balance between manageability and the Principle of Least Privilege.

          The example solution presented here offers a starting point. Keep in mind, though, that the hard-coded nature of the security protections using the [Authorize] attribute could easily become a code-maintenance nightmare if you get carried away. Also, adding the ability to create/edit "permissions" (which. remember, are actually "Roles" so far as the Identity framework is concerned) introduces a new convenience, and also a new dimension for trouble.

          I recommend that the creation, editing, and deletion of roles be limited to application developers only (create a special role/group just for them!), and only really as a means to add roles to the database after first adding them to the appropriate [Authorize] attributes in the code.

          I am most interested to hear feedback on this. Whether this was the exact solution you have been looking for, or you have spotted an idiotic, gaping flaw in my reasoning. Please feel free to comment, or shoot me an email at the address under the "About the Author" sidebar.

          Additional Resources and Items of Interest

          Viewing all 34 articles
          Browse latest View live