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)

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

Posted on January 5 2014 05:40 PM by jatten in ASP.NET MVC, ASP.Net, C#, CodeProject   ||   Comments (0)

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

Here is one way to do it.

Image by Elif Ayiter | Some Rights Reserved

The Example Project

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

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

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

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

Editor View Models and Editor Templates

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

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

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

 

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

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

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

The Core Domain Model

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

The Person Model:
public class Person
{
    public int Id { get; set; }
    public string LastName { get; set; }
    public string firstName { get; set; }
}

 

Add Test Data

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

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

 

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

Adding the Editor View Model

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

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

The Editor View Model class name must follow the convention ClassNameEditorViewModel

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

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

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

 

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

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

Adding the Wrapper View Model

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

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

 

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

Create an Editor View Template

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

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

Add an Editor Templates Folder to the Appropriate Directory:

add-the-editor-templates-folder

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

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

 

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

Adding the Index View

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

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

 

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

Tying it all together - the People Controller

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

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

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

 

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

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

Processing the Selection Results in the Submit Selected Method

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

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

Adding a Select All Checkbox to Toggle Selected Status

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

Modifications to Make the Demo Project Run (not required)

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

Make the Index View of the People the Default Home Page

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

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

 

Update the Route Configuration

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

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

 

Remove Unneeded Controllers and Views

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

Additional Resources and Items of Interest

 

Posted on January 5 2014 05:40 PM by jatten     

Comments (0)

About the author

My name is John Atten, and my "handle" on many of my online accounts is xivSolutions. I am Fascinated by all things technology and software development. I work mostly with C#, JavaScript/Node, and databases of many flavors. Actively learning always. I dig web development. I am always looking for new information, and value your feedback (especially where I got something wrong!). You can email me at:

jatten at typecastexception dot com

Web Hosting by