Sitecore Fields - cast and use

Posted 2/15/2012 by Przemyslaw Bednarek
Recently I was working on a tool that exports/imports content and resources in and out of sitecore for the purpose of managing translations. While doing some basic code operations I stumbled on a little catch with Sitecore Item's Fields.
 
Imagine you keep some bits of your content in a "Name Value Lists". While exporting those bits you would certainly like this to be presented in some sensible way and not as a query-stringish thing - which is how the value of the name value list field is kept internally. To do that, you have to know which field in fact is a Name Value List. What you probably know is that you can cast sitecore fields on any FieldType and you'll get something in return (not null) so you have to test the field against its .Type attribute:
myField.Type == "Name Value List"
So what I did is I took all the Items fields which are Name Value List's and casted them to MultilistField
var multiListFields = item.Fields.Where(p => p.Type == "Name Value List")
    .Cast<MultilistField>();
But the code above gave me null no matter how hard I focused my mental forces on it. And even despite the fact that Name Value Lists where actually present on the item! Using the Immediate Window I tried:
var singleOne = item.Fields.FirstOrDefault(p => p.Type == "Name Value List");
var castedOne = singleOne as MultilistField;
And what I got, was an exception in the second line. So when as threw me an exception I took a look inside the MultilistField and found out it doesn't inherit from the Field class, but instead it keeps (as apparently all CustomFields) the Field object as the InnerField property. That explains the exception. However knowing that even implicit casting of Fields works just fine I looked inside the MultilistField again and as expected found there an override of the implicit Cast operator. So in fact you cannot do:
var castedOne = myItem.Fields["MyList"] as MultilistField;
but you certainly can:
var castedTwo = (MultilistField) myItem.Fields["MyList"];
or:
MultilistField castedThree = myItem.Fields["MyList"]
However in the code above the behaviour is absolutely understandable, when you're using Linq extension methods it is not that obvious. In fact .Cast() and .OfType() methods internally use the as statement, and using these on sitecores FieldCollections will always give you null in the result, as inside it throws exceptions which Linq supresses. So the simple and akwardly looking solution was:
var multiListFields = item.Fields.Where(p => p.Type == "Name Value List")
    .Select(p=>(MultilistField)p);
But what seems to be a nicer solution if you plan to use these things quite often is to override the extension method .Cast() for all Custom Fields -> which is those that inherit from CustomField. I tried the following code:
public static IEnumerable<T> Cast<T>(this IEnumerable<Field> input) 
    where T : CustomField
{
    return input.Select(p => (T)p);
}
But it doesn't even compile. This is because CustomField is an abstract class! What does it mean in practice? Imagine trying
item.Fields.Cast<CustomField>()
Now, of course, you aren't really supposed to do that because you can never get an object of an abstract class. So, the only solution to make Sitecore Fields Casting easier in Linq is to override the Cast operator per each Custom Field Separately like:
public static IEnumerable<T> Cast<T>(this IEnumerable<Field> input)
    where T : MultilistField
{
    return input.Select(p => (T)p);
}
Which obviously isn't the most convenient thing to do, considering there is a number of Classes that inherit from CustomField. Personally, I preferred to stay with the first solution - without overriding extension methods.

Let me know if you have a different solution!
comments powered by Disqus

Przemysław Bednarek

Software Engineer

Archive