ASP.NET MVC 5 Identity: Extending and Modifying Roles

Posted on February 13 2014 09:17 PM by jatten in ASP.NET MVC, ASP.Net, C#, CodeProject   ||   Comments (8)

 

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:
public class 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
{
    public class ApplicationRole : IdentityRole
    {
        public ApplicationRole() : base() { }
        public ApplicationRole(string name, string description) : base(name)
        {
            this.Description = description;
        }
        public virtual string 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:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    if (modelBuilder == null)
    {
        throw new 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:
          public class 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();
          
            
          
            
          
              public bool RoleExists(string name)
          
              {
          
                  return _roleManager.RoleExists(name);
          
              }
          
            
          
            
          
              public bool CreateRole(string name, string description = "")
          
              {
          
                  // Swap ApplicationRole for IdentityRole:
          
                  var idResult = _roleManager.Create(new ApplicationRole(name, description));
          
                  return idResult.Succeeded;
          
              }
          
            
          
            
          
              public bool CreateUser(ApplicationUser user, string password)
          
              {
          
                  var idResult = _userManager.Create(user, password);
          
                  return idResult.Succeeded;
          
              }
          
            
          
            
          
              public bool AddUserToRole(string userId, string roleName)
          
              {
          
                  var idResult = _userManager.AddToRole(userId, roleName);
          
                  return idResult.Succeeded;
          
              }
          
            
          
            
          
              public void 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:
          public class 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;
          
              }
          
            
          
            
          
              public bool Selected { get; set; }
          
            
          
              [Required]
          
              public string RoleName { get; set; }
          
            
          
              // Add the new Description property:
          
              public string 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
          
          {
          
              public class 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)
          
                      {
          
                          return new 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:
          public void RemoveFromRole(string userId, string roleName)
          
          {
          
              _userManager.RemoveFromRole(userId, roleName);
          
          }
          
            
          
            
          
          public void 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:
          public class RoleViewModel
          
          {
          
              public string RoleName { get; set; }
          
              public string Description { get; set; }
          
            
          
              public RoleViewModel() { }
          
              public RoleViewModel(ApplicationRole role)
          
              {
          
                  this.RoleName = role.Name;
          
                  this.Description = role.Description;
          
              }
          
          }
          
            
          
            
          
          public class EditRoleViewModel
          
          {
          
              public string OriginalRoleName { get; set; }
          
              public string RoleName { get; set; }
          
              public string 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

           

          Posted on February 13 2014 09:17 PM by jatten     

          Comments (8)

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

          Posted on January 18 2014 08:10 AM by jatten in ASP.NET MVC, ASP.Net, C#, CodeProject, Web   ||   Comments (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:
          public class MailRecipientsViewModel
          
          {
          
              public List<SelectRecipientEditorViewModel> MailRecipients { get; set; }
          
              public MailRecipientsViewModel()
          
              {
          
                  this.MailRecipients = new List<SelectRecipientEditorViewModel>();
          
              }
          
            
          
            
          
              public IEnumerable<int> getSelectedRecipientIds()
          
              {
          
                  return (from r in this.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:
          public class Message
          
          {
          
              public MailRecipient Recipient { get; set; }
          
              public ApplicationUser User { get; set; }
          
              public string Subject { get; set; }
          
              public string 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:
          public class 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

           

          Posted on January 18 2014 08:10 AM by jatten     

          Comments (0)

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

          Posted on January 15 2014 08:26 PM by jatten in ASP.NET MVC, ASP.Net, C#, CodeProject, Web   ||   Comments (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
          
          {
          
              public class ApplicationUser : IdentityUser
          
              {
          
                  [Required]
          
                  public string FirstName { get; set; }
          
            
          
                  [Required]
          
                  public string LastName { get; set; }
          
            
          
                  [Required]
          
                  public string Email { get; set; }
          
              }
          
            
          
            
          
              public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
          
              {
          
                  public ApplicationDbContext()
          
                      : base("DefaultConnection")
          
                  {
          
            
          
                  }
          
              }
          
            
          
            
          
              public class IdentityManager
          
              {
          
                  public bool 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
          
          {
          
              public class 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]
          
                  public int MailRecipientId { get; set; }
          
            
          
                  [Required]
          
                  public string LastName { get; set; }
          
            
          
                  [Required]
          
                  public string FirstName { get; set; }
          
            
          
                  public string Email { get; set; }
          
            
          
                  public string Company { get; set; }
          
            
          
                  public string FullName
          
                  {
          
                      get
          
                      {
          
                          return string.Format("{0} {1}", this.FirstName, this.LastName);
          
                      }
          
                  }
          
            
          
            
          
                  public DateTime? getLastEmailDate()
          
                  {
          
                      var top = (from m in this.SentMails
          
                                 orderby m.SentDate descending
          
                                 select m).Take(1);
          
                      if (top.Count() > 0)
          
                      {
          
                          return top.ElementAt(0).SentDate;
          
                      }
          
                      else
          
                      {
          
                          return null;
          
                      }
          
                  }
          
              
          
                  public virtual 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]
          
                  public int MailId { get; set; }
          
            
          
                  [Required]
          
                  public int MailRecipientId { get; set; }
          
            
          
                  [Required]
          
                  public string SentToMail { get; set; }
          
            
          
                  [Required]
          
                  public string SentFromMail { get; set; }
          
            
          
                  [Required]
          
                  public System.DateTime SentDate { get; set; }
          
                
          
                  public virtual 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
          
          {
          
              public class 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)
          
                      {
          
                          return new 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)
          
                      {
          
                          return new 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)
          
                      {
          
                          return new 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");
          
                  }
          
            
          
            
          
                  protected override void 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:
          public class SelectRecipientEditorViewModel
          
          {
          
              public bool Selected { get; set; }
          
              public SelectRecipientEditorViewModel() { }
          
              public int MailRecipientId { get; set; }
          
              public string FullName { get; set; }
          
              public string Company { get; set; }
          
              public string Email { get; set; }
          
              public string 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:
          public class MailRecipientsViewModel
          
          {
          
              public List<SelectRecipientEditorViewModel> MailRecipients { get; set; }
          
              public MailRecipientsViewModel()
          
              {
          
                  this.MailRecipients = new List<SelectRecipientEditorViewModel>();
          
              }
          
            
          
            
          
              public IEnumerable<int> getSelectedRecipientIds()
          
              {
          
                  return (from r in this.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

           

          Posted on January 15 2014 08:26 PM by jatten     

          Comments (0)

          About the author

          My name is John Atten, and my username on many of my online accounts is xivSolutions. I am Fascinated by all things technology and software development. I work mostly with C#, Java, SQL Server 2012, learning ASP.NET MVC, html 5/CSS/Javascript. I am always looking for new information, and value your feedback (especially where I got something wrong!). You can email me at:

          jatten@typecastexception.com

          Web Hosting by