tutorials

Creating a Post Archive with the Ghost API and jQuery

I've long been missing an important feature in Ghost, my blog publishing platform: there's no inherent feature to create a post archive, or a list of all my posts in one place. I've gotten several requests for this feature, so I finally decided to just sit down and develop it using the Ghost Public API and a tiny bit of jQuery.

What follows is how I built my post archive the first time around. I have since replaced it with a Ghost-generated structure using the #get helper to solve some caching issues, but I like the look of the following system better. Please note that this post was written using Version 0.11.4 of Ghost and so it may change as Ghost changes.

Using jQuery and Ghost

Setting Up the Ghost Page Template

The first problem I had was that this page (the archive page) was not going to be either a post or a static page using my theme's default template. It was going to be a page, but it needed its own template, one that was very stripped-down.

Ghost supports using custom page templates by having a .hbs file that is prefixed with "page-". So, in my Ghost theme's root folder, I now have a file called page-all-posts.hbs, which looks like the following:

{{!< default}}

{{! This is a page template. A page outputs content just like any other post, and has all the same
    attributes by default, but you can also customise it to behave differently if you prefer. }}
{{#post}}
    <header class="post-header">
        <h1 class="post-title">{{title}}</h1>
    </header>
    <section class="post-content">
        {{content}}    
        <div id="postLoading">
            <h3>Loading post archive...</h3>
        </div>
        <div id="postList"></div>
    </section>
{{/post}}

The template is pretty darn simple. It has the "normal" Ghost stuff, like the {{#post}} tag, as well as the page title and content. The difference is what happens at the end: the div "postList" is where we will populate the list of all posts.

However, just having the template doesn't help us; I also need to create a static page with the same url as the name of the page template file. In my Ghost admin window, I created a new post, marked it as a static page, and gave it the URL "all-posts".

With that template and page set up, we can now write the jQuery to get us the actual posts.

Querying for All Posts

The first thing we have to do in order to display all posts is to get all the posts. I've already blogged about something like this when I implemented the 5 random posts sidebar, and so this solution will be very similar.

Ghost exposes a public API that can be used to query posts, users, and tags. In this case, I only want posts and I specifically want all my posts, so my query is very easy:

$(document).ready(function () {
    $.get(
        ghost.url.api('posts', {limit: 'all'})
    ).done(onSuccess);
});

The onSuccess method is really just a pass-through to another method, called showArchive:

function onSuccess(data) {  
    showArchive(data.posts);
    .... //Here's the code that does the 5 random posts sidebar
}

The real tricks begin when we implement the showArchive method.

Displaying the Posts

When we query for posts using the Ghost API, the posts object which is returned looks like this (I have simplified this schema):

"posts":[{
    "id":1,
    "title":"Welcome to Ghost",
    "slug":"welcome-to-ghost",
    "markdown":"...",
    "html":"...",
    "status":"published",
    "created_at":"2014-11-17T19:02:27.147Z",
    "created_by":1,
    "updated_at":"2014-11-17T19:02:27.147Z",
    "updated_by":1,
    "published_at":"2014-11-17T19:02:27.173Z",
    "published_by":1,
    "author":1,
    "url":"/welcome-to-ghost/"
  }, {
    "id":2,
    "uuid":"ac0a0374-a43c-15c4-391b-128d6bbba7c5",
    "title":"Lorem Ipsum Dolor",
    "slug":"lorem-ipsum-dolor",
    "markdown":"...",
    "html":"...",
    "status":"published",
    "created_at":"2014-11-18T19:02:27.147Z",
    "created_by":1,
    "updated_at":"2014-11-18T19:02:27.147Z",
    "updated_by":1,
    "published_at":"2014-11-18T19:02:27.173Z",
    "published_by":1,
    "author":1,
    "url":"/lorem-ipsum-dolor/"
  }],

From this, I can use the published_at property to get the month and year each post was published.

NOTE: The query we executed earlier already loads the posts in most-recent-first order, so we don't need to do any further ordering.

So, here' the outline of what we need the showArchive function to do:

  1. For each post, get the month and year that post was published.
  2. Each time the month or year changes, output a new subheader for that month and year.
  3. For each post, output a link to that post.

Here's the complete function:

function showArchive(posts) {  
    var monthNames = ["January", "February", "March", "April", "May", "June",
      "July", "August", "September", "October", "November", "December"
    ];
    var currentMonth = -1;
    var currentYear = -1;
    if(window.location.pathname == "/your-archive-page-url/"){ //Only display on this page
        $(posts).each(function(index,value){ //For each post 
            var datePublished = new Date(value.published_at); //Convert the string to a JS Date
            var postMonth = datePublished.getMonth();  //Get the month (as an integer)
            var postYear = datePublished.getFullYear(); //Get the 4-digit year

            if(postMonth != currentMonth || postYear != currentYear)
            { //If the current post's month and year are not the current stored month and year, set them
                currentMonth = postMonth;
                currentYear = postYear;
                //Then show a month/year header
                $("#postList").append("<br><span><strong>" + monthNames[currentMonth] + " " + currentYear + "</strong></span><br>");
            }
            //For every post, display a link.
            $("#postList").append("<span><a href='" + value.url +"'>" + value.title + "</a></span><br>");
        });
    }
}

I fully realize that this is not the best HTML (or Javascript, really), but it suits my purposes for now. Here's a screenshot of what this looks like on my blog:

Woohoo! I've got a working solution to show all my posts! Which would be great...except this isn't what I'm actually using now.

Using Ghost Only

The problem is that I use CloudFlare on this blog, and when doing so CloudFlare caches this page before the script has a chance to run. This results in an empty page, which is obviously not what I want. So, instead, I ended up going with a native Ghost solution, which looks something like this:

<div id="postList">  
    {{#get "posts" limit="all"}}
        {{#foreach posts}}
            {{#if featured}}
                 <span>
                     <span class='fa fa-star'></span>  
                     {{date published_at format="MMM DD, YYYY"}}:&nbsp;<a href="{{url}}">{{title}} </a>
                 </span>
                 <br>
            {{else}}
                <span>
                    {{date published_at format="MMM DD, YYYY"}}:  <a href="{{url}}">{{title}}</a>
                </span>
                <br>
                    {{/if}}
                {{/foreach}}
            {{/get}}
        </div>

Here's a little breakdown of what this does:

  • The {{get}} helper gets me all my posts.
  • Within the {{get}} context, the {{foreach}} helper loops through each post.
  • Within the {{foreach}} context, the {{if}} helper enables me to check if a particular post is featured, and if so, output a star icon.
  • The {{date}} helper allows me to get the post's publishing date and format it.
  • Finally, the {{url}} helper and the {{title}} helper output the post's URL and title respectively.

Here's how the native Ghost solution looks:

The major downside to the native Ghost solution is that I no longer have the month and year section headers, something I would rather like to have. But, the page is no longer cached by CloudFlare, so it works. Further, this solution is much cleaner than the jQuery solution.

At any rate, now I've got a working page that lists all my blog posts! Check it out!

Summary

Please let me know if you found this post useful, and share other interesting tips about Ghost in the comments! Check out the Ghost documentation to learn about the other helpers they have available.

Happy Coding!

Mapping DataTables and DataRows to Objects in C# and .NET

My group regularly uses DataSet, DataTable, and DataRow objects in many of our apps.

(What? Don't look at me like that. These apps are old.)

Anyway, we're simultaneously trying to implement good C# and object-oriented programming principles while maintaining these old apps, so we often end up having to map data from a data set to a C# object. We did this enough times that I and a coworker (we'll call her Marlena) decided to sit down and just make up a new mapping system for use with these DataTable and DataRow objects.

As always with my code-based posts, there's a GitHub project with a full working example app, so check that out too!

One Jump Ahead

So here's a basic problem with mapping from DataSet, DataTable, and DataRow objects: we don't know at compile time what columns and tables exist in the set, so mapping solutions like AutoMapper won't work for this scenario. Our mapping system will have to assume what columns exist. But, in order to make it more reusable, we will make the mapping system return default values for any values which it does not locate.

There's also another, more complex problem: the databases we are acquiring our data from use many different column names to represent the same data. 20 years of different maintainers and little in the way of cohesive naming standards will do that do a database. So, if we needed a person's first name, the different databases might use:

  • first_name
  • firstName
  • fname
  • name_first

This, as might be imagined, makes mapping anything rather difficult. So our system will also need to be able to map from many different column names.

Finally, this system wouldn't be worth much if it couldn't handle collections of objects as well as single objects, so we'll need to allow for that as well.

So, in short, our system needs to:

  1. Map from DataTable and DataRow to objects.
  2. Map from multiple different column names.
  3. Handle mapping to a collection of objects as well as a single object.

We'll need several pieces to accomplish this. But before we can even start building the mapping system, we must first acquire some sample data.

Mine, Mine, Mine

We're going to create some DataSet objects that we can test our system against. In the real world, you would use an actual database, but here (for simplicity's sake) we're just going to manually create some DataSet objects. Here's a sample class which will create two DataSet objects, Priests and Ranchers, each of which use different column names for the same data:

public static class DataSetGenerator  
{
    public static DataSet Priests()
    {
        DataTable priestsDataTable = new DataTable();
        priestsDataTable.Columns.Add(new DataColumn()
        {
            ColumnName = "first_name",
            DataType = typeof(string)
        });
        priestsDataTable.Columns.Add(new DataColumn()
        {
            ColumnName = "last_name",
            DataType = typeof(string)
        });
        priestsDataTable.Columns.Add(new DataColumn()
        {
            ColumnName = "dob",
            DataType = typeof(DateTime)
        });
        priestsDataTable.Columns.Add(new DataColumn()
        {
            ColumnName = "job_title",
            DataType = typeof(string)
        });
        priestsDataTable.Columns.Add(new DataColumn()
        {
            ColumnName = "taken_name",
            DataType = typeof(string)
        });
        priestsDataTable.Columns.Add(new DataColumn()
        {
            ColumnName = "is_american",
            DataType = typeof(string)
        });

        priestsDataTable.Rows.Add(new object[] { "Lenny", "Belardo", new DateTime(1971, 3, 24), "Pontiff", "Pius XIII", "yes" });
        priestsDataTable.Rows.Add(new object[] { "Angelo", "Voiello", new DateTime(1952, 11, 18), "Cardinal Secretary of State", "", "no" });
        priestsDataTable.Rows.Add(new object[] { "Michael", "Spencer", new DateTime(1942, 5, 12), "Archbishop of New York", "", "yes" });
        priestsDataTable.Rows.Add(new object[] { "Sofia", "(Unknown)", new DateTime(1974, 7, 2), "Director of Marketing", "", "no" });
        priestsDataTable.Rows.Add(new object[] { "Bernardo", "Gutierrez", new DateTime(1966, 9, 16), "Master of Ceremonies", "", "no" });

        DataSet priestsDataSet = new DataSet();
        priestsDataSet.Tables.Add(priestsDataTable);

        return priestsDataSet;
    }

    public static DataSet Ranchers()
    {
        DataTable ranchersTable = new DataTable();
        ranchersTable.Columns.Add(new DataColumn()
        {
            ColumnName = "firstName",
            DataType = typeof(string)
        });
        ranchersTable.Columns.Add(new DataColumn()
        {
            ColumnName = "lastName",
            DataType = typeof(string)
        });
        ranchersTable.Columns.Add(new DataColumn()
        {
            ColumnName = "dateOfBirth",
            DataType = typeof(DateTime)
        });
        ranchersTable.Columns.Add(new DataColumn()
        {
            ColumnName = "jobTitle",
            DataType = typeof(string)
        });
        ranchersTable.Columns.Add(new DataColumn()
        {
            ColumnName = "nickName",
            DataType = typeof(string)
        });
        ranchersTable.Columns.Add(new DataColumn()
        {
            ColumnName = "isAmerican",
            DataType = typeof(string)
        });

        ranchersTable.Rows.Add(new object[] { "Colt", "Bennett", new DateTime(1987, 1, 15), "Ranchhand", "", "y" });
        ranchersTable.Rows.Add(new object[] { "Jameson", "Bennett", new DateTime(1984, 10, 10), "Ranchhand", "Rooster", "y" });
        ranchersTable.Rows.Add(new object[] { "Beau", "Bennett", new DateTime(1944, 8, 9), "Rancher", "", "y" });
        ranchersTable.Rows.Add(new object[] { "Margaret", "Bennett", new DateTime(1974, 7, 2), "Bar Owner", "Maggie", "y" });
        ranchersTable.Rows.Add(new object[] { "Abigail", "Phillips", new DateTime(1987, 4, 24), "Teacher", "Abby", "y" });

        DataSet ranchersDataSet = new DataSet();
        ranchersDataSet.Tables.Add(ranchersTable);

        return ranchersDataSet;
    }
}

We'll test our system against this sample data.

Something There

Now we can build our actual mapping solution. First off, we need a way to decide what column names map to object properties. It was Marlena's idea to keep those things together, and so we came up with a class called DataNamesAttribute that looks like this:

[AttributeUsage(AttributeTargets.Property)]
public class DataNamesAttribute : Attribute  
{
    protected List<string> _valueNames { get; set; }

    public List<string> ValueNames
    {
        get
        {
            return _valueNames;
        }
        set
        {
            _valueNames = value;
        }
    }

    public DataNamesAttribute()
    {
        _valueNames = new List<string>();
    }

    public DataNamesAttribute(params string[] valueNames)
    {
        _valueNames = valueNames.ToList();
    }
}

This attribute can then be used (in fact, can only be used, due to the AttributeUsage(AttributeTargets.Property) declaration) on properties of other classes. Let's say we're going to map to a Person class. We would use DataNamesAttribute like so:

public class Person  
{
    [DataNames("first_name", "firstName")]
    public string FirstName { get; set; }

    [DataNames("last_name", "lastName")]
    public string LastName { get; set; }

    [DataNames("dob", "dateOfBirth")]
    public DateTime DateOfBirth { get; set; }

    [DataNames("job_title", "jobTitle")]
    public string JobTitle { get; set; }

    [DataNames("taken_name", "nickName")]
    public string TakenName { get; set; }

    [DataNames("is_american", "isAmerican")]
    public bool IsAmerican { get; set; }
}

Now that we know where the data needs to end up, let's start mapping out the mapper (heh).

Reflection

Our mapper class will be a generic class so that we can map from DataTable or DataRow objects to any kind of object. We'll need two methods to get different kinds of data:

public class DataNamesMapper<TEntity> where TEntity : class, new()  
{
    public TEntity Map(DataRow row) { ... }
    public IEnumerable<TEntity> Map(DataTable table) { ... }
}

Let's start with the Map(DataRow row) method. We need to do three things:

  1. Figure out what columns exist in this row.
  2. Determine if the TEntity we are mapping to has any properties with the same name as any of the columns (aka the Data Names) AND
  3. Map the value from the DataRow to the TEntity.

Here's how we do this, using just a bit of reflection:

public TEntity Map(DataRow row)  
{
    //Step 1 - Get the Column Names
    var columnNames = row.Table.Columns
                               .Cast<DataColumn>()
                               .Select(x => x.ColumnName)
                               .ToList();

    //Step 2 - Get the Property Data Names
    var properties = (typeof(TEntity)).GetProperties()
                                      .Where(x => x.GetCustomAttributes(typeof(DataNamesAttribute), true).Any())
                                      .ToList();

    //Step 3 - Map the data
    TEntity entity = new TEntity();
    foreach (var prop in properties)
    {
        PropertyMapHelper.Map(typeof(TEntity), row, prop, entity);
    }

    return entity;
}

Of course, we also need to handle the other method, the one where we can get a collection of TEntity:

public IEnumerable<TEntity> Map(DataTable table)  
{
    //Step 1 - Get the Column Names
    var columnNames = table.Columns.Cast<DataColumn>().Select(x => x.ColumnName).ToList();

    //Step 2 - Get the Property Data Names
    var properties = (typeof(TEntity)).GetProperties()
                                        .Where(x => x.GetCustomAttributes(typeof(DataNamesAttribute), true).Any())
                                        .ToList();

    //Step 3 - Map the data
    List<TEntity> entities = new List<TEntity>();
    foreach (DataRow row in table.Rows)
    {
        TEntity entity = new TEntity();
        foreach (var prop in properties)
        {
            PropertyMapHelper.Map(typeof(TEntity), row, prop, entity);
        }
        entities.Add(entity);
    }

    return entities;
}

You might be wondering just what the heck the PropertyMapHelper class is. If you are, you might also be about to regret it.

Dig a Little Deeper

The PropertyMapHelper, as suggested by the name, maps values to different primitive types (int, string, DateTime, etc.). Here's that Map() method we saw earlier:

public static void Map(Type type, DataRow row, PropertyInfo prop, object entity)  
{
    List<string> columnNames = AttributeHelper.GetDataNames(type, prop.Name);

    foreach (var columnName in columnNames)
    {
        if (!String.IsNullOrWhiteSpace(columnName) && row.Table.Columns.Contains(columnName))
        {
            var propertyValue = row[columnName];
            if (propertyValue != DBNull.Value)
            {
                ParsePrimitive(prop, entity, row[columnName]);
                break;
            }
        }
    }
}

There are two pieces in this method that we haven't defined yet: the AttributeHelper class and the ParsePrimitive() method. AttributeHelper is a rather simple class that merely gets the list of column names from the DataNamesAttribute:

public static List<string> GetDataNames(Type type, string propertyName)  
{
    var property = type.GetProperty(propertyName).GetCustomAttributes(false).Where(x => x.GetType().Name == "DataNamesAttribute").FirstOrDefault();
    if (property != null)
    {
        return ((DataNamesAttribute)property).ValueNames;
    }
    return new List<string>();
}

The other we need to define in ParsePrimitive(), which as its name suggests will parse the values into primitive types. Essentially what this class does is assign a value to a passed-in property reference (represented by the PropertyInfo class). I'm not going to post the full code on this post (you can see it over on GitHub), so here's a snippet of what this method does:

private static void ParsePrimitive(PropertyInfo prop, object entity, object value)  
{
    if (prop.PropertyType == typeof(string))
    {
        prop.SetValue(entity, value.ToString().Trim(), null);
    }
    else if (prop.PropertyType == typeof(int) || prop.PropertyType == typeof(int?))
    {
        if (value == null)
        {
            prop.SetValue(entity, null, null);
        }
        else
        {
            prop.SetValue(entity, int.Parse(value.ToString()), null);
        }
    }
    ...
}

That's the bottom of the rabbit hole, as it were. Now, we can use the DataSet objects we created earlier and our mapping system to see if we can map this data correctly.

Two Worlds

Here's a quick program that can test our new mapping system:

class Program  
{
    static void Main(string[] args)
    {
        var priestsDataSet = DataSetGenerator.Priests();
        DataNamesMapper<Person> mapper = new DataNamesMapper<Person>();
        List<Person> persons = mapper.Map(priestsDataSet.Tables[0]).ToList();

        var ranchersDataSet = DataSetGenerator.Ranchers();
        persons.AddRange(mapper.Map(ranchersDataSet.Tables[0]));

        foreach (var person in persons)
        {
            Console.WriteLine("First Name: " + person.FirstName + ", Last Name: " + person.LastName
                                + ", Date of Birth: " + person.DateOfBirth.ToShortDateString()
                                + ", Job Title: " + person.JobTitle + ", Nickname: " + person.TakenName
                                + ", Is American: " + person.IsAmerican);
        }

        Console.ReadLine();
    }
}

When we run this app (which you can do too), we will get the following output:

Which is exactly what we want!

(I mean, really, did you expect me to blog about something that didn't work?)

Go the Distance

It concerns me that this system is overly complicated, and I'd happily take suggestions on how to make it more straightforward. While I do like how all we need to do is place the DataNamesAttribute on the correct properties and then call an instance of DataNamesMapper<T>, I feel like the whole thing could be easier somehow. Believe it or not, this version is actually simpler than the one we're actually using in our internal apps.

Also, check out the sample project over on GitHub, fork it, test it, whatever. If it helped you out, or if you can improve it, let me know in the comments!

Finally, extra special bonus points will go to anyone who can figure out a) what the hell those odd section titles are about and b) where I got the sample data from.

Happy Coding!

A Simple Caching Scheme for Web API using Dependency Injection

I use Dependency Injection (DI) quite a bit in my ASP.NET projects, particularly in Web API and MVC web applications. Recently, I had a need to implement a caching layer in one of my MVC apps, and such a layer would be best used if it could be injected into my clients layer (e.g. the layer that called an API and handled responses from the same). The solution I came up with seemed to be pretty simple, so I wanted to share it here.

In this post, we're going to set up a simple MVC project that consumes a Web API and implements a caching layer.

Requirements

For this project, I'm using two of my favorite NuGet packages:

The sample project, which is over on GitHub, is a fully-functional implementation of the strategy described in this post. Feel free to check it out, branch it, download it, whatever!

Setting Up the API

First, let's take a look at our sample API. Here's the class DateNumberObject, which represents a response returned from the API to the web project:

public class DateNumberObject  
{
    public DateTime CurrentDate { get; set; }
    public int RandomNumber { get; set; }

    public DateNumberObject()
    {
        CurrentDate = DateTime.Now;
        Random rand = new Random();
        RandomNumber = rand.Next(1, 200);
    }
}

As you can see, all this class does is return the current date and time and a random number.

We can now build our API, which is also rather simple. Here's a snippet from the API's controller:

[RoutePrefix("samples")]
public class SampleController : ApiController  
{

    [HttpGet]
    [Route("date")]
    public IHttpActionResult GetDateAndNumber()
    {
        return Ok(new DateNumberObject());
    }
}

That's all our API will do: return the current date and a random number. Since there's no caching taking place at the service layer, we will need to implement caching at the web layer.

Speaking of the web layer, in our sample solution it is an MVC5 project, and we will be using my favorite Dependency Injection tool, StructureMap.

Setting up StructureMap

If you've never set up StructureMap in your MVC projects, you'll want to read this section; otherwise, skip to the next section.

The first thing we need to do is download the StructureMap.MVC5 NuGet package, which will add a DependencyInjection folder and a StructuremapMvc file to our app:

Inside the Dependency Resolution folder will be a file called DefaultRegistry.cs, which will initially look something like this:

namespace WebApiCacheDemo.Mvc.DependencyResolution {  
    using Caching;
    using StructureMap.Configuration.DSL;
    using StructureMap.Graph;

    public class DefaultRegistry : Registry {
        #region Constructors and Destructors
        public DefaultRegistry() {
            Scan(
                scan => {
                    scan.TheCallingAssembly();
                    scan.WithDefaultConventions();
                    scan.With(new ControllerConvention());
                });
        }
        #endregion
    }
}

The way StructureMap works is that it keeps instances of classes that need to be "injected" into other classes in a Container class, and uses these instances any time a particular interface is called for in another class. You'll see exactly what this means in the next section.

The Uncached Example

Let's build the uncached example first, and we can begin by setting up the MVC client. For this project we'll be using RestSharp (which I have also written about before) to consume the API responses, so be sure to download the NuGet package.

Because we're using Dependency Injection, we need our clients to be injectable into our controllers. For this reason, we will make both an interface and a class for our client, like so:

public interface ISampleClient : IRestClient  
{
    DateNumberObject GetSampleDateAndNumberUncached();
}

public class SampleClient : RestClient, ISampleClient  
{
    public SampleClient()
    {
        BaseUrl = new Uri("http://localhost:58566/");
    }

    public DateNumberObject GetSampleDateAndNumberUncached()
    {
        RestRequest request = new RestRequest("samples/date", Method.GET);
        var response = Execute<DateNumberObject>(request);
        return response.Data;
    }
}

Note that we don't need to register this client with StructureMap because the naming follows the standard conventions (ISampleClient maps to SampleClient).

Now we need our controller...

[RoutePrefix("Home")]
public class HomeController : Controller  
{
    private ISampleClient _sampleClient;

    public HomeController(ISampleClient sampleClient)
    {
        _sampleClient = sampleClient;
    }

    [HttpGet]
    [Route("Uncached")]
    [Route("")]
    [Route("~/")]
    public ActionResult Uncached()
    {
        var model = _sampleClient.GetSampleDateAndNumberUncached();
        return View(model);
    }
}

...which returns a view:

@model WebApiCacheDemo.Contracts.Samples.DateNumberObject
@{
    ViewBag.Title = "Uncached Date Sample";
}

<h2>Uncached Results</h2>  
<div class="row">  
    <span><strong>Current Date:</strong></span>
    @Model.CurrentDate
</div>  
<div class="row">  
    <span><strong>Random Number:</strong></span>
    @Model.RandomNumber
</div>  

When we run this, we can see that the view returns the newest date and time, as shown in this gif:

That's exactly what we would expect to see, of course. Now, we can get down to implementing a cache at the web layer for this.

The Cached Example

The first thing we need is our CacheService interface and implementation, which I totally stole from this StackOverflow answer:

public class InMemoryCache : ICacheService  
{
    public T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class
    {
        T item = MemoryCache.Default.Get(cacheKey) as T;
        if (item == null)
        {
            item = getItemCallback();
            MemoryCache.Default.Add(cacheKey, item, DateTime.Now.AddMinutes(30));
        }
        return item;
    }
}

public interface ICacheService  
{
    T GetOrSet<T>(string cacheKey, Func<T> getItemCallback) where T : class;
}

We also need our SampleClient class updated to use this cache service, like so:

public class SampleClient : RestClient, ISampleClient  
{
    private ICacheService _cache;
    public SampleClient(ICacheService cache)
    {
        _cache = cache;
        BaseUrl = new Uri("http://localhost:58566/");
    }

    public DateNumberObject GetSampleDateAndNumber()
    {
        return _cache.GetOrSet("SampleDateAndNumber", () => GetSampleDateAndNumberUncached());
    }

    public DateNumberObject GetSampleDateAndNumberUncached()
    {
        RestRequest request = new RestRequest("samples/date", Method.GET);
        var response = Execute<DateNumberObject>(request);
        return response.Data;
    }
}

NOTE: In my structure, I am intentionally implementing the cached and uncached operations as separate methods in the same client, so that either one can be used. This may or may not be the correct usage in your application.

Further, we need to update our controller:

[RoutePrefix("Home")]
public class HomeController : Controller  
{
    ...

    [HttpGet]
    [Route("Cached")]
    public ActionResult Cached()
    {
        var model = _sampleClient.GetSampleDateAndNumber();
        return View(model);
    }
}

The last step is to register the ICacheService implementing in StructureMap's DefaultRegistry.cs class:

namespace WebApiCacheDemo.Mvc.DependencyResolution {  
    ...

    public class DefaultRegistry : Registry {
        public DefaultRegistry() {
            ...
            var inMemoryCache = new InMemoryCache();
            For<ICacheService>().Use(inMemoryCache);
        }
    }
}

Now, when we run this sample, we will see the cached values for the date and number, as shown in the following gif:

Now we've successfully implemented our cache AND used Dependency Injection in the process!

Summary

With this structure, we've successfully implemented a caching layer into our MVC application, all while using StructureMap to provide Dependency Injection. This structure allows us to potentially swap cache providers (in case we, say, move to a database-located cache for server farms) without too much trouble, as well as injecting the cache service into our existing clients.

Don't forget to check out the sample project over on GitHub, and feel free to point out how you've used this implemention or where it can be improved in the comments.

Happy Coding!

Working with Environments and Launch Settings in ASP.NET Core

I'm continuing my series of deep dives into ASP.NET Core, on my never-ending quest to keep my coding career from becoming obsolete. In this part of the series, let's discuss a new feature in ASP.NET Core that will make debugging and testing much more intuitive and pain-free: Environments and Launch Settings.

ASP.NET Core includes the native ability to modify how an application behaves (and what it displays) based upon the environment it is currently executing in. This ends up being quite useful, since now we can do things as simple as not showing the error page to people using the application in a production environment. But it requires just a tiny bit of setup, and some knowledge of how environments are controlled.

ASPNETCORE_ENVIRONMENT

At the heart of all the stuff ASP.NET Core can do with environments is the ASPNETCORE_ENVIRONMENT environment variable. This variable controls what type of environment the current build will run in. You can edit this variable manually by right-clicking your project file and selecting "Properties", then navigating to the "Debug" tab.

There are three "default" values for this variable:

  • Development
  • Staging
  • Production

Each of those values corresponds to certain methods we can use in our Startup.cs file. In fact, the Startup.cs file includes several extension methods that make modifying our application based it's current environment quite simple.

Selecting an Environment

The Startup.cs file that every ASP.NET Core application needs does many things, not the least of which is setting up the environment in which the application runs. Here's a snippet of the default Startup.cs file that was generated when I created my sample project for this post:

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)  
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Notice the IHostingEnvironment variable. That variable represents the current environment in which the application runs, and ASP.NET Core includes four extension methods that allow us to check the current value of ASPNETCORE_ENVIRONMENT:

  • IsDevelopment()
  • IsStaging()
  • IsProduction()
  • IsEnvironment()

Notice what we're setting up in the Startup.cs file: if the current environment is Development, we use the Developer Exception Page (which was formerly the Yellow Screen of Death) as well as enabling Browser Link in Visual Studio, which helps us test our application in several browsers at once.

But we don't want those features enabled in a Production environment, so instead our configuration establishes an error handling route "/Home/Error".

This is the coolest thing about this feature: you can decide what services are enabled for your application in each environment without ever touching a configuration file!

However, what if you need more environments? For example, at my day job we have up to four environments: Development, Staging, Production, and Testing, and very often I'll need to change application behaviors or display based on which environment it's currently running in. How do I do that in ASP.NET Core?

The launchSettings.json File

ASP.NET Core 1.0 includes a new file called launchSettings.json; in any given project, you'll find that file under the Properties submenu:

This file sets up the different launch environments that Visual Studio can launch automatically. Here's a snippet of the default launchSettings.json file that was generated with my demo project:

{
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "CoreEnvironmentBehaviorDemo": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

At the moment we have two profiles: one which runs the app using IIS Express and one which runs the app using a command-line handler. The interesting thing about these two profiles is that they correspond directly to the Visual Studio play button:

In other words: the launchSettings.json file controls what environments you can run your app in using Visual Studio. In fact, if we add a new profile, it automatically adds that profile to the button, as seen on this GIF I made:

All this together means that we can run the app in multiple different environment settings from Visual Studio, simply by setting up the appropriate launch profiles.

The Environment Tag Helper

Quite a while ago I wrote a post about Tag Helpers and how awesome they are, and I want to call out a particular helper in this post: the <environment> tag helper.

With this helper, we can modify how our MVC views are constructed based upon which environment the application is currently running in. In fact, the default _Layout.cshtml file in Visual Studio 2015 comes with an example of this:

<environment names="Development">  
    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>
</environment>  
<environment names="Staging,Production">  
    <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
            asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
            asp-fallback-test="window.jQuery">
    </script>
    <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/bootstrap.min.js"
            asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
            asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal">
    </script>
    <script src="~/js/site.min.js" asp-append-version="true"></script>
</environment>  

In this sample, when we run the app in Development mode we use the local files for jQuery, Bootstrap, and our custom Javascript; but if we're running in the Staging or Production environments, we use copies of those libraries files found on the ASP.NET content delivery network (CDN). In this way, we improve performance, because a CDN can deliver these static files from one of many centers around the globe, rather than needing to round-trip all the way to the source server to get those files.

But, remember, in our sample we have the custom environment Testing. The environment tag helper also supports custom environments:

<div class="row">  
    <environment names="Development">
        <span>This is the Development environment.</span>
    </environment>
    <environment names="Testing">
        <span>This is the Testing environment.</span>
    </environment>
    <environment names="Production">
        <span>This is the Production environment.</span>
    </environment>
</div>  

Summary

Working with environments (both default and custom) gets a whole lot easier in ASP.NET Core, and with the new Launch Settings feature, getting the app started in different environment modes is a breeze! Using these features, we can:

  • Create and use custom environments.
  • Enable or disable application services based on the environment the app is running in.
  • Use the environment tag helper (<environment>) to modify our MVC views based on the current execution environment.

You can check out my sample project over on GitHub to see this code in action. Feel free to mention any cool tips and tricks you've found when dealing with environments and launch settings in the comments!

Happy Coding!