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.)
Now playing:
Mott the Hoople - All the Way from Memphis
(from Greatest Hits)
2 Responses
#1 Dew Drop - January 14, 2009 | Alvin Ashcraft's Morning Dew said...
14-Jan-09 12:36 PMPingback from Dew Drop - January 14, 2009 | Alvin Ashcraft's Morning Dew
#2 Writing an Archive Calendar, Part 2 said...
18-Jan-09 1:57 PMThank you for submitting this cool story - Trackback from DotNetShoutout
Leave a response
Note: some MarkDown is allowed, but HTML is not. Expand to show what's available.
_emphasis_
**strong**
[text](url)
`IEnumerable`
* an item
1. an item
> Now is the time...
Preview of response