Day 19: Long-Running Notifications Using Badges and Entity Framework Code First

This is an installment in a 30 day series on Bootstrap and the MVC Framework. To see more, check out Day 0 for an index.

We previously looked at using TempData to store alerts, which is great when what we are trying to alert the user to is a transient message that lives only for the next request. What about the scenario where we have long running notifications? Unread email? Outstanding actions the user must follow up on?

Today we’re going to bang out a solution here…in the interest of time and simplicity this will not be in line with best practices, but come back tomorrow for a discussion on how to get closer to them.

Rocking Out A Model

If we want a way to indicate that the user needs to take care of something, we need two pieces: some kind of UI to point it out – which we have with badges – and some place to store the data – which we’ll get with EF.  Right-click on your Models directory and add a new class, called Notification, and add the following code to it:

    public enum NotificationType
    {
        Registration,
        Email
    }

    public class Notification
    {
        public int NotificationId { get; set; }
        public string Title { get; set; }
        public NotificationType NotificationType { get; set; }
        public string Controller { get; set; }
        public string Action { get; set; }
    }
These are _just_ classes, but when we lean on Entity Framework, we get a database out of it as well. In the “code first” approach, we first write ourselves a class that represents the data, as we’ve done above. Next, we have to let EF know that we expect a storage mechanism for that data, namely, we create a data context and a DB set (representing a table) inside of another class. Add SiteDataContext as a class in your Models folder and add the following code to it:
    public class SiteDataContext : DbContext
    {
        public SiteDataContext() : base("DefaultConnection") { }

        public DbSet<Notification> Notifications { get; set; }
    }
There’s not much to it: DbSet represents a table and DbContext identifies this as a class that represents a connection to the database. If the DB doesn’t exist, EF creates it for us. We call the base class constructor with a string, which identifies the connection string to use in the web.config file, if present, or the name of the DB that will be created if it doesn’t. At this point, **build your project **using Shift+Ctrl+B or by pressing F6\. We need the app compiled to take advantage of tooling in Entity Framework. Now, go to the Package Manager Console (View –> Other Windows –> Package Manager Console) and type the following command:
Enable-Migrations -ContextTypeName SimpleSite.Models.SiteDataContext

You’ll need to make sure that the namespace and class name match what is specified above. This command generates a configuration file that has an empty Seed method (rather, it has some comments in it, but you can delete them).  This file can be used by Entity Framework as a way to do advanced configuration, set your own conventions for table naming, or, in our case, seeding the database with some test data.  Paste in the following code in the Seed method:

protected override void Seed(SimpleSite.Models.SiteDataContext context)
{
context.Notifications.AddOrUpdate(notification => notification.Title,
new Notification
{
Title = “John Smith was added to the system.”,
NotificationType = NotificationType.Registration
},
new Notification
{
Title = “Susan Peters was added to the system.”,
NotificationType = NotificationType.Registration
},
new Notification
{
Title = “Just an FYI on Thursday’s meeting”,
NotificationType = NotificationType.Email
});
}

AddOrUpdate is like an “upsert” method you can use to inject or update data in your DB.  It accepts a lambda expression that specifies how unique rows are identified, and a parameter array which is a list of objects you want to inject into the specified table.

Updating our Database

With support for the migrations in place, we want to create an initial version of the DB and apply it in our development environment. From the Package Manager Console, type these lines of code:

Add-Migration db-create

Update-Database

Have a look through the migration class that is generated. Pretty cool, eh? Although this is just a first stab at it, it really shows how you can customize the build up – or tear down – of you tables. It’s worth a whole book of content, though, so I’m not diving in for now! The call to Update-Database executes the Up() method on all outstanding migrations. Everything is tracked for you by EF in the database.

Okay, Here’s One Best Practice

One thing that I really like about MVC and EF is how easy it is to get data into your view using the entities of your database. One thing that I really hate about MVC and EF is how easy it is to get data into your view using the entities of your database. For reals. It’s great for demos and terrible for production.

Instead, use a view model…a way to decouple your view from your database completely. Get in this habit as early as you can, and then come back and thank me 6 months into the maintenance portion of your contract.

Add another class called NotificationViewModel and write in the following code:

public class NotificationViewModel
{
public int Count { get; set; }
public string NotificationType { get; set; }
public string BadgeClass { get; set; }
}

Rather than relying on the data in the database we’re going to use a projection of the data instead. This can help to shield our UI from changes in the model and eliminate the risk of excess DB access or over-exposing sensitive data. It also allows for other best practices we’ll cover in the days ahead.

Updating Our Controller

Update your Index() action to be the following:

public ActionResult Index()
{
var context = new SiteDataContext();

var notifications = context.Notifications
    .GroupBy(n =&gt; n.NotificationType)
    .Select(g =&gt; <span class="kwrd">new</span> NotificationViewModel
    {
        Count = g.Count(),
        NotificationType = g.Key.ToString(),
        BadgeClass = NotificationType.Email == g.Key
            ? <span class="str">"success"</span>
            : <span class="str">"info"</span>
    });

ViewBag.Notifications = notifications;

<span class="kwrd">return</span> View();

}

We’re getting the list of notifications out of the DB, grouping them by type, specifying the style class to use and counting them op.  We then set that information in the ViewBag, which is accessible from our views. Note that this approach only works for now in our Index method, and only on the HomeController. Don’t worry, we’ll take care of that tomorrow. Smile

Lighting up Notifications in the View

Find the UL element with the class “nav navbar-nav” that contains the LI elements that compose the menu (these are around line 26 for me). At the end of the LI elements, we’re going to inject a few more, so we iterate over the list of notifications that we popped into the ViewBag like so:

@foreach (NotificationViewModel notification in ViewBag.Notifications)
{
<li><a href=“#”>@notification.NotificationType <span class=“badge badge-@notification.BadgeClass>@notification.Count</span></a></li>
}

We’ve got some more work to do to fully get these working, but you will likely see the direction this is moving in by now, and users will see that something needs to be done about all those…notifications.

image

Next Steps

Well, as I said we’ve got some clean up to do to get a little closer to best practices. Our models are in our web site project and unable to be reused. We have to put code in each and every single action on every controller where we would want to see notifications. Our layout is getting polluted with additional code. There’s no way to resolve notifications. We’re in bad shape!

Check back tomorrow so that we can resolve some of the low hanging fruit and discuss what remains to get some of this train on the rails.