Singleton page
A singleton page extension method looks for the first page of a particular type under given root page. Usually, you would like to search under start page. There are extensions for ContentReference and for PageData.
var testPage1 = ContentReference.StartPage.GetSingletonPage<TestPage>();
var startPage = _contentLoader.Get<StartPage>(ContentReference.StartPage);
var testPage2 = startPage.GetSingletonPage<TestPage>();
But it is also possible to search under any page. For example, in a commerce solution you might have a Checkout page with Order confirmation and Order summary pages as children.
var checkoutPage = ContentReference.StartPage.GetSingletonPage<CheckoutPage>();
var orderConfirmationPage = checkoutPage.GetSingletonPage<OrderConfirmationPage>();
var orderSummaryPage = checkoutPage.GetSingletonPage<OrderSummaryPage>();
A singleton page API under the hood uses simple concurrent dictionary to cache content references for particular singleton page under the root page. This allows to search for the page only once and next time use cached ContentReference.
An API also allows to create and inject your own caching mechanism. For example, you would like to use EPiServer's CacheManager or fake cache for testing.
public class FakeCache : DefaultContentReferenceCache
{
public ConcurrentDictionary<CacheKey, ContentReference> InternalCache => Cache;
}
...
var fakeCache = new FakeCache();
Extensions.InjectedCache = new Injected<IContentReferenceCache>(fakeCache);
Currently, the project does not have a NuGet package, but it is quite easy to copy/paste an extension code from GitHub. The project also has tests which could be used as a reference.
Summary
Using this API you can create a project without the need to configure singleton pages or use API as a fallback when the page is not configured. Per Nergård recently wrote an article how to limit page types and block types to be created only once. Combining both techniques creates a good solution for managing singleton pages.