After my previous post on the EPiSocial plugin, Paul Smith pointed out that I should consider using Page Objects (PO) rather than plain Dynamic Data Store (DDS) to store page related data. The upside of using PO is that it provides support for mirroring and import/export. The downside being that it handles things on a page by page basis which makes queries over multiple pages a bit tricky. This is necessary when doing things like getting pages with the highest rating or getting pages with most comments.
Luckily, the process of changing the plugin from using DDS to PO is as simple as creating new implementations of the ICommentRepository and IRatingRepository interfaces.
After some minor changes from the previous version they look like this:
In this post I will focus on the PO implementation of ICommentRepository.
The first thing I noted was that the documentation for PO was rather slim. Nothing in the SDK, all I found was a single Tech Note that described the features briefly.
One thing that differs when using PO instead of DDS is that a PO is stored as a single object per page. To be able to store multiple comments for a single page I created a container object to hold the comments.
8:this.Comments = new List<DynamicDataStoreComment>();
The PageObjectCommentRepository takes two dependencies. The first being a CommentMapper that handles the mapping described below. The second one is a PageObjectManagerFactory that handles creation of PageObjectManager objects based on page id.
Before saving comments to the DDS the PageComment object is mapped to a DynamicDataStoreComment object that implements the necessary IDynamicData interface. The reverse thing happens when getting comments from the store. This mapping might seem unnecessary, why don´t use the DynamicDataStoreComment directly instead? There are good reasons.
One being that I want the plugin to be extendable. If I or someone else wanted to create a different implementation using a different storage, that implementation probably won´t care about EPiServer.Data.
Another reason is that testing code using the API should not have to know about EPiServer.Data either.
1:publicvoid AddComment(PageComment pageComment)
3: var pageObjectManager = this.pageObjectManagerFactory.GetPageObjectManagerForPage(pageComment.PageId, pageComment.PageLanguage);
4: var ddsComment = this.commentMapper.MapToDynamicDataStoreComment(pageComment);
5: var poCommentContainer = pageObjectManager.Load<PageObjectCommentContainer>(CommentKey);
7:if (poCommentContainer == null)
9: poCommentContainer = new PageObjectCommentContainer();
When removing a comment we first have to load the object, remove the comment from the list and save it back again. I also found that I had to delete the comment directly from the DDS for it to disappear completely, otherwise it will still be around when making queries like getting the latest comments below.
1:publicvoid RemoveComment(string commentId)
3: Identity identity;
5:if (!Identity.TryParse(commentId, out identity))
7:thrownew InvalidOperationException("Comment id is not an Identity.");
10: var ddsComment = commentStore.Load<DynamicDataStoreComment>(identity);
11: var pageObjectManager = this.pageObjectManagerFactory.GetPageObjectManagerForPage(ddsComment.PageId, ddsComment.PageLanguage);
12: var poCommentContainer = pageObjectManager.Load<PageObjectCommentContainer>(CommentKey);
So far it´s been pretty straight forward. But since the PageObjectManager works on a per page basis, this kind of query cannot be done using it. But since PO uses DDS in the background, we can create a query using DDS directly to get the latest comments:
Unfortunately, due to a bug in the DDS implementation we cannot call Take() directly but have to call ToList() first. This is not optimal and I´m hoping this is fixed in the next release of EPiServer.
Wire it up
Now that we have our new PO repository ready we have to tell the application that we wish to use it instead of the default DDS implementation.
EPiSocial uses StructureMap to wire up it´s dependencies. To override the default setup we have to add a StructureMap registry of our own and the plugin will automatically pick it up during it´s initialization. So we add the following registry to the web application:
1:publicclass PageObjectRegistry : Registry
For more on StructureMap check out this post by Stefan.
That´s all, we are now ready to use the PO version of the plugin.
An updated version of the plugin can be downloaded here. It now contains a EPiSocial.PageObjects assembly with the new PO implementation.