Sitefinity offers many extension points to its core content types, as well as letting you create completely customized content types of your own via the Module Builder. You can add custom fields to existing types like News and Events, or add as many fields as you like to custom content types. Working with these custom properties in code can seem a little daunting at first: How do you do it? What data types do you work with? Which methods do you call on your objects? There are several ways of accessing certain kinds of properties, while others you have to know the type of object you’re getting or else you’ll get errors at runtime. This post will show you how to access a couple of the most common types of custom properties, which work both for custom fields on built-in types and fields for custom content types created through the Module Builder. It will also touch upon how to discover unknown types in code without needing prior knowledge of said custom fields (other than their names).
Defining Custom Properties in Sitefinity
I won’t delve too deeply in how to set up custom properties in Sitefinity since that’s beyond the scope of this post. On most of the Content pages (Content Blocks, News, Events, etc.) you can find a “Custom Fields for {type of content}” link. This will take you to a screen where you can define new custom properties, specifying name, type, whether or not they’re required, and other details. When creating new types from the Module Builder the interface is very much the same. This post will assume this step has already been taken, and will explain the next step: accessing these custom fields in code.
Testing the Waters Before Taking the Plunge
If you are writing generic code that fetches custom properties (to perform generic validation, for example) and you won’t know what the property name is until runtime, you can use the method DoesFieldExist() to validate the given property name. If you call myContentItem.GetValue(“PropertyThatDoesNotExist”), you’ll get a runtime exception. Instead, call myContentItem.DoesFieldExist(“PropertyThatDoesNotExist”), which returns false. From there you can program around the case of being given a non-existing field. This can also be used in scenarios where you have multiple environments, where one has a new field and another doesn’t have it yet. With DoesFieldExist() you can safely transition your code base without having to maintain separate versions.
String Properties
Now we can move on to working with known, existing properties. The most common type of property I find myself using is of type string. Their use ranges from simple short fields like a name, to large chunks of data containing HTML. Whether you’re working with Short Text or Long Text custom properties, the method of accessing them is the same, as at the end of the day they are both considered to be of the same type.
However, while the type stored in the database is a string, Sitefinity treats it as an “Lstring.” Lstring, or Localizable String, is the type Sitefnity treats custom string fields as, since it’s what Sitefinity uses to maintain a value in multiple languages. When working with simple strings you typically just want the main string value (especially if you’re not concerned with multilingual values at this point), and luckily Sitefinity implemented Lstring so that it implicitly converts to a string. This means that the following code snippet:
Lstring myLstring = new Lstring(); string myRegularString = myLstring;
is legal code that can run without errors at compile-time or runtime.
With that in mind, there are two primary methods of fetching string properties. The first uses the same method used for fetching most properties: myContentItem.GetValue<string>(“PropertyName”). This returns a “string” type object and ignores Lstring completely. The second is myContentItem.GetString(“PropertyName”). This function returns an Lstring, but as shown above can be immediately assigned to a string variable.
When to Use GetValue<string>() vs. GetString()
At first glance, you’d think that the two methods are interchangeable. However, there are circumstances where one method will cause an error while the other doesn’t, and other circumstances where the opposite is true. This issue primarily arises due to the previously-mentioned implicit conversation that Lstring has with string. While the compiler will always accept this, at runtime issues can arise.
Use GetValue<string>() for Queries
Consider the following: We have a “PressRelease” custom content type, with, among other custom properties, a string property “Title.” We have a PressRelease with title “Foo” that we wish to fetch from Sitefinity, so we write the following code:
Type pressReleaseType = TypeResolutionService .ResolveType("Telerik.Sitefinity.DynamicTypes.Model.PressReleases.PressRelease"); DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager(); DynamicContent pressRelease = dynamicModuleManager.GetDataItems(pressReleaseType) .FirstOrDefault(d => d.GetString("Title") == "Foo");
This code compiles and looks correct: Compare the Title string field with the title we want to fetch, and return the result. But when we try to run it, however:
When querying using custom properties, you have to use GetValue() instead:
DynamicContent pressRelease = dynamicModuleManager.GetDataItems(pressReleaseType) .FirstOrDefault(d => d.GetValue<string>("Title") == "Foo");
Note that this seems to be the only expression that Sitefinity will accept. I tested out many other expressions, and all yielded different runtime exceptions:
- d.GetValue<Lstring>(“Title”) == “Foo”
- d.GetValue<Lstring>(“Title”).ToString() == “Foo”
- d.GetValue<Lstring>(“Title”) == new Lstring(“Foo”)
- d.GetValue(“Title”).ToString() == “Foo”
Use GetString() for Retrieving Values
After you have your DynamicContent object retrieved, you’ll want to get the actual properties from it. In order to fetch the title, one would think to use the same method as they did in the FirstOrDefault() query:
string title = pressRelease.GetValue<string>("Title");
But then you’re greeted with another runtime error:
You have to use GetString() here instead in order to fetch the property without throwing any exceptions:
// GetString() returns Lstring, which implicitly converts to string in this statement. string title = pressRelease.GetString("Title");
In both of the instances where an exception was thrown, the implicit conversion from Lstring to string cannot take place. In the first, the query is being passed back to SQL Server (or whatever your backing data store happens to be), which removes C# from the equation and leaves it unable to do the conversion. In the second, the method GetValue() doesn’t use implicit conversion, since not every instance of T will be implicitly convertible from the object given.
String Custom Properties Summary
Here’s a complete simple example of all of the correctly-working code snippets from above, from querying custom properties to accessing their data. We also add in an additional Where() omitted above for brevity that assures we’re getting the Live version of the content item. Remember: GetValue(“PropertyName”) in your LINQ queries, and GetString(“PropertyName”) when retrieving the values.
Type pressReleaseType = TypeResolutionService .ResolveType("Telerik.Sitefinity.DynamicTypes.Model.PressReleases.PressRelease"); DynamicModuleManager dynamicModuleManager = DynamicModuleManager.GetManager(); DynamicContent pressRelease = dynamicModuleManager.GetDataItems(pressReleaseType) .Where(d => d.Status == ContentLifecycleStatus.Live && d.Visible) .FirstOrDefault(d => d.GetValue<string>("Title") == "Foo"); string title = pressRelease.GetString("Title");
“Number” Custom Properties
When specifying a numeric type for custom properties there are a couple primary considerations: Do we allow fractional numbers, and is the number required. In these basic cases that leaves us with up to four possible combinations. Regardless of the options selected, however, Sitefinity returns these properties as the same type in C#. Sitefinity will always return the decimal type for custom properties that are numbers.
It should be noted that the limitations prescribed to the custom properties, the backing data store and code is unaffected. The limitations/requirements are all enforced on the Sitefinity site itself. This means that in code you have to be careful. If you attempt to get a value of type “decimal” instead of “decimal?” and there’s no value, you’ll get a runtime exception. If the value is required, however, you can generally get away with not using the nullable decimal type, simplifying your code.
In the following example we’ve added a few custom properties to our custom content type. SubscriberCount and NumberOfWidgets represent whole numbers, while Price and Discount represent fractional. Note the use of decimal? vs. decimal:
decimal subscriberCount = pressRelease.GetValue<decimal>("SubscriberCount"); // Required, whole numbers only decimal price = pressRelease.GetValue<decimal>("Price"); // Required, limited to 2 decimal places decimal? numberOfWidgets = pressRelease.GetValue<decimal?>("NumberOfWidgets"); // Not required, whole numbers only decimal? discount = pressRelease.GetValue<decimal?>("Discount"); // Not required, limited to 2 decimal places.
Querying Numeric Custom Properties
While there is no equivalent built-in method to “GetString()” for numeric types, there is still a small distinction made between querying for numeric custom properties and fetching them. When querying by numeric properties you must use the nullable type even if the value can never be null. If you write the following, you will get an exception:
DynamicContent pressRelease = dynamicModuleManager.GetDataItems(pressReleaseType) .FirstOrDefault(d => d.GetValue<decimal>("Price") > 2.50m);
To query your content items by a custom numeric property, use the nullable deicmal type:
DynamicContent pressRelease = dynamicModuleManager.GetDataItems(pressReleaseType) .FirstOrDefault(d => d.GetValue<decimal?>("Price") > 2.50m);
Determining Types of Unknown Custom Properties
Sitefinity’s GetValue() method does not require a type parameter in order to function when retrieving values. When no type is specified, it will return your value in the form of System.Object. We can use this to our advantage when we’re not aware of what data type our custom property is and want to know exactly what type to use. Now most of the time, you won’t need to do this. After reading this post you are already aware of the most two common types (strings and decimals). But if you’re ever in a scenario where the type is unknown, and you don’t want to go through several build -> run -> fail cycles, debugging and simply getting the object and inspecting the type can be much quicker.
Other Custom Property types
There are several other kinds of properties you can create, ranging from Classifications (i.e. Tags and Categories) to Related Media (i.e. Images, Videos, Documents). Accessing these fields are more complex than reading the right property using the right type, and will be shown in a future blog post. Stay tuned!
The post Retrieving Simple Custom Properties from Sitefinity Content Items appeared first on Falafel Software Blog.