Correctly Provisioning Managed Metadata Columns

Last year I was brought in to help rescue a large and badly floundering project. The system in question made use of managed metadata in several areas and the original developers used Ari Bakker’s blog and code as their basis, specifically the code and in this excellent blog post. Ari began his journey with this excellent blog post from Wictor Wilén. Over time we had sporadic issues with upgrade scenarios and duplicate hidden note fields, but these were pretty low on the list of issues we needed to deal with before the system could go live (it was a real mess) and I never had the chance to dig in and figure it out.

Recently my friend and fellow MVP Dan Attis asked me to take a look at a similar issue he was facing with a sandbox solution. For whatever reason, the sandbox’s behavior was slightly different from the equivalent farm code. It had the same sort of problem I’d seen on the rescue project, but in the sandbox case it was worse as it prevented his affected features from activating. Together, Dan and I found the issue which was an error in Ari’s post. Before I get into that, having a look at the post in question is definitely worthwhile as what I have written here and my sample code is based upon his good work (which in turn is based on Wiktor’s), and I won’t be rehashing everything he wrote.

The Mistakes

Ari’s mistake comes with his step 3.

You should not do this. SharePoint will do it for you. If you do, SharePoint will create a duplicate with a different internal name in a farm solution and fail when you try to use an associated content type in a sandbox solution. Instead you need to provide enough information for SharePoint to create the required hidden note field as follows.

The Value attribute above will cause SharePoint to automatically provision a hidden note field with an ID of {2702cde3-3435-0227-be7b-e86d5f1250cc}.

Later in his post Ari writes about the creation of a Content Type that contains the metadata column and includes the hidden note field as one of the field references. You should not do this! Below is a sample content type inherits from the Document content type and includes the Color field shown above.

At this point my sample diverges from Ari’s because instead of using a list definition I use a ListInstance, a ContentTypeBinding, and a feature event receiver instead of a list definition. I do so because I think it’s easier, I am lazy, and I need the receiver anyway as my sample illustrates the use of metadata navigation in the resulting library. More on that later…

Timing is Everything

For my example to work I need to control the order of events so that:

  1. SharePoint provisions the Field and Content Type
  2. A feature event receiver connects the field to the correct term set
  3. The Content Type is bound to the list

If step 3 happens before step 2, the resulting list field(s) will not be connected to the term store. In Dan’s case this didn’t matter because in sandbox mode you have to wire up the metadata by hand as the required DLLs are not available in the sandbox.

The Sample

You can download the sample code from http://www.elumenotion.com/Downloads/MetadataSample.zip. It contains three features.

Visual Studio activates them in the order indicated above because of the way I’ve configured the package, but in real life I’d have a fourth feature to control the order of activation.

  • FieldsAndTypes includes the Field and ContentType elements shown in the screen shots above.
  • TermSetConfig creates a term set named Colors and connects the Color field to the new term set. This code is stolen borrowed directly from Ari and Wiktor with some minor changes and so I won’t cover it here.
  • SampleDocumentLibrary creates the sample library, binds my content type and then executes code to configure the metadata navigation settings for the new library.

The ListInstance and ContentTypeBinding look like this:

The feature event receiver does the following steps.

  1. Delete the original document content type from the library.
  2. Configure the metadata navigation settings.
  3. Configure the default view to display the Color column

public class SampleDocumentLibraryEventReceiver : SPFeatureReceiver

{

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWeb web = (SPWeb)properties.Feature.Parent;
SetupSampleDocumentLibrary(web);
}

private void SetupSampleDocumentLibrary(SPWeb web)
{
SPList docs = web.Lists["Library with Metadata"];

    docs.ContentTypes[0].Delete();

    var navSettings = MetadataNavigationSettings.GetMetadataNavigationSettings(docs);
navSettings.ClearConfiguredHierarchies();
navSettings.ClearConfiguredKeyFilters();
AddKeyFilter(docs, navSettings, "Color");
AddHierarchy(docs, navSettings, "Color");
MetadataNavigationSettings.SetMetadataNavigationSettings(docs, navSettings, true);

    docs.RootFolder.Update();
SPView view = docs.DefaultView;
view.ViewFields.DeleteAll();
view.ViewFields.Add("DocIcon");
view.ViewFields.Add("LinkFilename");
view.ViewFields.Add("Color");
view.Query = @"<OrderBy><FieldRef Name='FileLeafRef' Ascending='TRUE' /></OrderBy>";
view.Update();
}

private static void AddHierarchy(SPList matters, MetadataNavigationSettings navSettings, string fieldName)
{
MetadataNavigationHierarchy hierarchy = new MetadataNavigationHierarchy(matters.Fields[fieldName]);
navSettings.AddConfiguredHierarchy(hierarchy);
}

private static void AddKeyFilter(SPList matters, MetadataNavigationSettings navSettings, string fieldName)
{
MetadataNavigationKeyFilter filterNavigation = new MetadataNavigationKeyFilter(matters.Fields[fieldName]);
navSettings.AddConfiguredKeyFilter(filterNavigation);
}

 

Finally the screen shots below show the sample in action.

Adding a document


Metadata Navigation


 

Happy SharePointing!!!
–Doug

Author: Doug Ware

Atlanta based entrepreneur, author of many SharePoint books and videos, leader of Atlanta .NET user group, founder of InstantQuick, and SharePoint MVP.

6 Comments

  • Michael Blumenthal, Magenic (blog.blumenthalit.com)

    May 17, 2012, 4:45 pm

    Doug,
    What is the minimal set of field attributes needed for this to work correctly? For example, do we really need to specify the List, WebId, SourceId, and xmlns or will SharePoint provide correct values of these by default?
    Why are some of the attributes and subtags not recognized as valid in Visual Studio? Is there a way to fix that?
    Thanks!

  • Doug

    May 17, 2012, 7:11 pm

    I don’t really know because I haven’t tested it, but I believe you need them all…

    As far as VS is concerned I am not sure what you mean. Are you saying you see red squiglies indicating a schema violation? I don’t…

  • NST2

    May 31, 2012, 6:04 am

    Dear Doug,
    while I am currently creating loads of content types / list definitions using managed metadata fields based on Aris blogpost, I stumbled upon your comment on his blog and felt over your (at least not for me) not working correction:

    What you describe here is just to leave out the hidden note field. »The Value attribute above« just links the TaxonomyFieldType(Multi) field to its hidden note field, but does not provide enough information to let SharePoint create this field.

    Is it possible, that you mixed up left over _0 fields from a last deployment with your assumption ?

    And last but not least:
    Where did the TaxCatchAll and TaxCatchAllLabel fields go in your suggestion ? Following your description will result in a non working solution.

    Cheers

  • --Doug

    July 17, 2012, 9:28 am

    Sorry for not replying sooner. The comment spammers have been really bad and I missed this.

    Did you download the sample?

    I promise it works as I described, but if you see this, please send me what you have done. There may be something subtle we can learn from it.

  • Jørn Cornelius Olsen

    August 29, 2012, 12:25 pm

    Hi Doug.

    While I generally agree with your conclusion that the hidden note field should not be provisioned, I would like to mention a couple of pitfalls that I have spent far too much time working out:
    1) If you go the long way and try to use the declarative approach and create a field definition plus a field instance, it is important to note that the field must NOT be defined within schema.xml like every other field should. If you do anyway, the inner mechanics break and you get the following error when editing the properties of an item: Failed to get value of the “field-name” column from the “Managed Metadata” field type control. See details in log. Exception message: Invalid field name. {text-field-guid}
    2) I have yet to figure out how I can make the title field required, since to make that work I must declare the content type with Inherits=”FALSE” which in turn breaks the managed metadata field just like i 1).
    Hoping to save some people a few hours (days?) of troubleshooting.

  • Pablo Igualada

    November 5, 2012, 11:32 am

    I have discovered the problem of declaratively add note field.

    Its Name and StaticName has to be a Guid.ToString(“N”), with the first character being a letter (a-f).
    The problem of not following this convention is that everything works, but Refinements, as Search Gatherer does not gather this property correctly, because it has hardcoded so that it need this Regex:

    Regex.IsMatch(strPropertyName, “ows_[a-pA-F][0-9a-fA-F]{31}”

    Thanks of course to Microsoft guys for not document this!!

Comments are closed