Note: This article was originally published in February 2009 on labs.episerver.com. It was based on EPiServer CMS 5 R1. I’ve re-published it here due to popular demand. :)
Dynamic properties versus page properties
As you probably know, dynamic properties are like page properties in many ways, except they do not need to be set specifically on a per-page basis.
Dynamic property values are inherited in the page tree. This means that if a certain page does not have a value set for a dynamic property, EPiServer will move up the page tree until it finds a page that does.
In many programming scenarios you don’t have to worry about whether a property is a dynamic property or a page property. For example, you could use the following to retrieve a string property value:
var myPropertyValue = page.Property["MyStringPropertyName"].Value as string ?? string.Empty;
Determine if a dynamic property is inherited or set explicitly for the current page
However, you may sometimes find yourself in a situation where you do want to be able to tell the difference between an inherited dynamic property value and a dynamic property value that has been explicitly set for a specific page.
Performance considerations
The DynamicProperty class, part of the EPiServer.DataAbstraction namespace, is a non-cached API that shouldn’t be used other than for “administrative purposes” (see the SDK for details).
This means that you could incur a performance penalty on your website if you perform a lot of dynamic property lookups using the DynamicProperty class. So, if you need to use the DynamicProperty class for large numbers of dynamic property lookups – do implement some sort of caching!
Dynamic properties and multiple languages
The DynamicProperty class can be used to retrieve a dynamic property like so:
DynamicProperty myDynamicProperty = DynamicProperty.Load(CurrentPage.PageLink, "MyDynamicProperty");
However, since the Load() method only accepts a PageReference object and a property name, you don’t have any control over the language branch in cases where the dynamic property is language-specific.
I’ll demonstrate a way around this below.
Implementing a HasDynamicPropertyDefined() method
I’ll create a method which returns true if the specified dynamic property has been set explicitly on a page and false if the dynamic property hasn’t been set (or if it’s “only” inherited).
The pseudo-code looks like this:
- Check if the lookup result has been cached
- If so, return the cached value
- If not, load the dynamic property
- Check the dynamic property’s status
- If the status is Defined then return true, otherwise return false
The Status property of a DynamicProperty object is of the DynamicPropertyStatus enum type. Other valid values are Inherited and Undefined.
1. Check the cache
First off we’ll determine the correct cache key to check if the cache has the answer we’re looking for (which would save us a few database calls):
//Construct a cache key for storing previous dynamic property lookups
var cacheKey = string.Format("{0}-{1}-{2}", page.PageLink.ID,page.LanguageBranch,propertyName);
//Check if a cache value exists
if (HttpRuntime.Cache[cacheKey] != null)
{
//Verify the cache value is a boolean
if (HttpRuntime.Cache[cacheKey] is bool) //Cache hit
{
return (bool)HttpRuntime.Cache[cacheKey];
}
else
{
throw new InvalidCastException("Incorrect cache value type.");
}
}
else
{
//Cache miss, perform dynamic property lookup}
}
Note: the cache key should vary by page id, language branch and property name!
2. Get the cache timeout setting from web.config
If we get a cache miss we’ll start out by trying to retrieve and parse a cache timeout setting from the <appSettings> element in web.config:
int cacheTimeOut;
//Verify the cache timeout setting exists in <appSettings>
if (ConfigurationManager.AppSettings["Timeout"] == null)
{
throw new Exception("Cache timeout setting missing.", null);
}
//Try to parse the cache timeout setting
if(!int.TryParse(ConfigurationManager.AppSettings["Timeout"], out cacheTimeOut))
{
throw new Exception("Incorrect cache timeout setting.", null);
}
//Verify cache timeout value
if (cacheTimeOut < 0)
{
throw new ArgumentOutOfRangeException("Timeout must be above 0.");
}
Now the cacheTimeOut variable should contain a value indicating how long we keep dynamic property lookups in the cache.
3. Perform the dynamic property lookup to determine if it has been set for the current page
I’ll start out by retrieving all dynamic properties for the current page:
//Get all dynamic properties for the current page
var properties = DynamicProperty.ListForPage(page.PageLink);
Next I’ll use LINQ to query the dynamic property collection for the dynamic property which has the correct name and has been explicitly set on the current page for the specified language branch (determined by the PageData object called page below):
// Query the properties collection to find a dynamic property
// with the specified name and which has been defined on the current page
// for the current language
var dynProp = from DynamicProperty p in col
where p.PropertyValue.Name == propertyName &&
(p.LanguageBranch == page.LanguageBranch || p.LanguageBranch == string.Empty) &&
p.Status == DynamicPropertyStatus.Defined
select p;
Now, if we get one result from the LINQ expression above we can safely say that we have a dynamic property set for the current page (and the current language branch) and not a “sneaky” inherited value:
bool result = dynProp.Count() == 1;
4. Add the result to the cache
As I mentioned earlier, the DynamicProperty.Load() and DynamicProperty.ListForPage() methods are non-cached. So, we want to use our cache key to store our result in the cache to prevent unnecessary dynamic property lookups:
//Add the result to the cache
HttpRuntime.Cache.Add(
cacheKey,
result,
null,
DateTime.Now.AddSeconds(cacheTimeOut),
Cache.NoSlidingExpiration,
CacheItemPriority.Normal,
null);
5. Finish up and return the result
Now we’re all done, and our HasDynamicPropertyDefined() method may return the result of all its hard work:
return result;
The HasDynamicPropertyDefined() method is an extension method
If you haven’t started using extension methods yet – do it! They’ll make your life a whole lot easier!
As you can see below, the HasDynamicPropertyDefined() is a static method (which is in turn part of a static class – this is important) which will automatically “attach itself” to all objects of type PageData.
So, whenever I’m working with a PageData object (the CurrentPage property in this case) I can now use the following syntax:
bool dynamicPropertyExplicitlySet = CurrentPage.HasDynamicPropertyDefined("MyPropertyName");
The secret lies within the method head of the HasDynamicPropertyDefined() method:
public static bool HasDynamicPropertyDefined(this PageData page, string propertyName)
{
// Method body
}
The first parameter (identified by adding the this keyword) will not be available when calling the method. Instead this method declaration states that all PageData objects should have a method called HasDynamicPropertyDefined() which accepts a single parameter called propertyName.
Good luck and happy coding! Don’t hesitate to comment if you have any questions, suggestions or just want to say hi! :)