Writing an Archive Calendar, part 2

Next up in this series on writing the calendar of archived posts, is the PostsReader class. This class queries the database for the posts for a particular month or a particular day. It utilizes a couple of other helper classes that manage the caching of results, so we'll look at these first.

The first helper class is the PostCollectionSet. This is a set of already-computed collections of posts and is the granularity with which post collections are stored in the application cache. The set is declared as generic dictionary.

  public class PostCollectionSet : Dictionary<PostCollectionKey, PostCollection> {
    static string CacheKey = "jmbPostCollectionSet";

    public static PostCollectionSet GetInstance() {
      PostCollectionSet set = null;
      HttpContext context = HttpContext.Current;
      if (context == null)
        set = new PostCollectionSet();
      else {
        set = (PostCollectionSet)context.Cache[CacheKey];
        if (set == null) {
          set = new PostCollectionSet();
          context.Cache.Add(CacheKey, set, null, DateTime.Now.AddMinutes(10.0), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
        }
      }
      return set;
    }
  }

In essence, the GetInstance() method, queries the current HttpContext for the cache dictionary, and adds a new set if there isn't one present yet. The expiration is set to 10 minutes.

The PostCollectionKey class used as the key type in this dictionary is simple too: it's merely a class that stores the date of a post collection, with a special GetHashCode() method so it acts as a good key for this particular dictionary.

  public class PostCollectionKey {
    private int year;
    private int month;
    private int day;
    public PostCollectionKey(int year, int month, int day) {
      this.year = year;
      this.month = month;
      this.day = day;
    }
    public override int GetHashCode() {
      return (year * 1000) + (month * 100) + day;
    }
  }

Now we can look at the PostsReader class.

  public class PostsReader {

    private static Query BuildRangePostsQuery(DateTime start, DateTime end) {
      Query q = Post.CreateQuery();
      Column published = new Column("Published", DbType.DateTime, typeof(DateTime), "Published", false, false);
      Column isPublished = new Column("IsPublished", DbType.Boolean, typeof(bool), "IsPublished", false, false);
      Column isDeleted = new Column("IsDeleted", DbType.Boolean, typeof(bool), "IsDeleted", false, false);
      Column categoryID = new Column("CategoryID", DbType.Int32, typeof(Int32), "CategoryID", false, false);
      // this ordering is required by PostCount()
      q.OrderByDesc(published);
      q.AndWhere(published, start, Comparison.GreaterOrEquals);
      q.AndWhere(published, end, Comparison.LessThan);
      q.AndWhere(isPublished, true, Comparison.Equals);
      q.AndWhere(isDeleted, false, Comparison.Equals);
      q.AndWhere(categoryID, 1, Comparison.NotEquals); // uncategorized
      return q;
    }

    private static Query BuildMonthPostsQuery(int year, int month) {
      DateTime startOfMonth = new DateTime(year, month, 1);
      DateTime startofNextMonth = startOfMonth.AddMonths(1);
      return BuildRangePostsQuery(startOfMonth, startofNextMonth);
    }

    private static Query BuildDayPostsQuery(int year, int month, int day) {
      DateTime startOfDay = new DateTime(year, month, day);
      DateTime startofNextDay = startOfDay.AddDays(1);
      return BuildRangePostsQuery(startOfDay, startofNextDay);
    }

    public static PostCollection GetPostsForMonth(int year, int month) {
      return GetPostsForDate(year, month, 0);
    }

    public static PostCollection GetPostsForDate(int year, int month, int day) {
      PostCollectionSet set = PostCollectionSet.GetInstance();
      PostCollectionKey key = new PostCollectionKey(year, month, day);
      PostCollection posts = null;
      if (set.ContainsKey(key)) {
        posts = set[key];
      }
      else {
        if (day == 0)
          posts = PostCollection.FetchByQuery(BuildMonthPostsQuery(year, month));
        else
          posts = PostCollection.FetchByQuery(BuildDayPostsQuery(year, month, day));
        set[key] = posts;
      }
      return posts;
    }
  }

The interesting (and boring at the same time) method here is the BuildRangePostsQuery() method. It takes two dates (a start and end date), and constructs a query that can be used to fetch all of the published posts in that date range, ordered in descending order of published date/time, and not deleted or belonging to category 1 (the uncategorized category).

After that one, the next one is the workhorse GetPostsForDate() method. This works out if the requested post collection is in the cache, and if not, builds the query, executes it, and both stores the result in the cache and returns it to the caller.

(Part 1 is here, part 2 here, part 3 here, part 4 here, part 4a here, part 4b here.)

Album cover for Greatest Hits Now playing:
Mott the Hoople - All the Way from Memphis
(from Greatest Hits)



Posts on similar topics...

Share it: Digg It!  StumbleUpon  Reddit  Del.icio.us  NewsVine  Furl  BlinkList  Ma.gnolia  Technorati

2 Responses

  • Wed 14 Jan 2009
  • 12:36 PM
  •  avatar #1

Dew Drop - January 14, 2009 | Alvin Ashcraft's Morning Dew said...

Pingback from Dew Drop - January 14, 2009 | Alvin Ashcraft's Morning Dew

  • Sun 18 Jan 2009
  • 1:57 PM
  •  avatar #2

Writing an Archive Calendar, Part 2 said...

Thank you for submitting this cool story - Trackback from DotNetShoutout

Leave a Response

About Me

I'm Julian M Bucknall, the M because it's my middle initial and because I and the other Julian Bucknall (the movie guy) would like to differentiate ourselves.

I'm a programmer by trade, an actor by ambition, and an algorithms guy by osmosis. I write articles for PCPlus in my spare time, not that there's much of that.

Julian M Bucknall Apart from that, an ex-pat Brit, atheist, microbrew enthusiast, Pet Shop Boys fanboy, slide rule and HP calculator collector, amateur photographer, Altoids muncher.

DevExpress

I'm Chief Technology Officer at Developer Express, a software company that writes some great controls and tools for .NET and Delphi. I'm responsible for the technology oversight and vision of the company.

The OUT Campaign

The OUT Campaign

Validation

Valid XHTML 1.0 Transitional     Valid CSS!

Bottom swirl

Archives

February 2010 (2)
SMTWTFS
« Jan  
123456
78910111213
14151617181920
21222324252627
28

Like this Archive Calendar widget? Download it here.

Search

Google ads

My Tweets

  • It seems replacing all our windows will take a mere couple of days. Next Monday/Tuesday will be interesting: it'll drive me crazy.
  • Every now and then, you have a day where nothing goes right. That was today. Bah.
  • White paper written. Now to write the exemplar application
Bottom swirl