SharePoint Config

Ari Bakker's thoughts on customising and configuring SharePoint

Provisioning SharePoint 2010 Managed Metadata fields

with 53 comments

The first part of this two part series discussed some of the problems with provisioning SharePoint 2010 managed metadata fields. This post will cover a robust method of deploying SharePoint 2010 managed metadata columns in a way that avoids common errors and enables the columns to automatically show up in the search refinement panel.

To recap on the previous post we are aiming to avoid the following problems when creating SharePoint 2010 taxonomy fields through features:

  1. If you create a list definition that uses this site column you get the error: Failed to get value of the “{0}” column from the “Managed Metadata” field type control. See details in log. Exception message: Invalid field name. {00000000-0000-0000-0000-000000000000}.
  2. The managed metadata column does not appear in the search refinement panel as per columns created through the UI.
  3. If the managed metadata features are not activated in the site the field will not work (and no obvious errors will be displayed).

We found the first problem was due to a missing note field when creating a list definition that used the site column. The second problem was due to the missing TaxCatchAll and TaxCatchAllLabel columns and missing event receivers on the list definition. The last issue can be easily avoided by adding a feature dependency to ensure the required feature is activated before we deploy our custom site column. With this information we can use the following steps to deploy managed metadata fields that avoid these issues. I’ve also created an example project that I’ve included at the end of the article if you want to see this working for yourself.

Create a managed metadata field

  1. Provision a managed metadata field using type TaxonomyFieldType (or TaxonomyFieldTypeMulti to allow multiple values)
  2. Configure the managed metadata column to reference an existing term set
  3. Provision a hidden field of type Note
  4. Configure the managed metadata column to use the hidden Note field
  5. Ensure that the TaxonomyFieldAdded feature is activated on the site collection

Create a list definition that uses a managed metadata field

  1. Create a content type that uses the managed metadata fields and the TaxCatchAll columns
  2. Create a list definition that uses the content type and includes all metadata and TaxCatchAll columns
  3. Attach the TaxonomyItemSynchronousAddedEventReceiver and TaxonomyItemUpdatingEventReceiver event receivers to the list definition
  4. Create a list instance to provision instances of the list definition

Create a managed metadata field

1. Provision a managed metadata field

The first step is to create a site column using the Field element with a Type of either TaxonomyFieldType (allowing a single selection) or Type TaxonomyFieldTypeMulti (allowing multiple selections if you also set Mult=”True”).

<Field ID="{F88D530B-705F-488A-9069-FAF5C79B61F7}"
    Type="TaxonomyFieldType"
    DisplayName="Regional Office"
    ShowField="Term1033"
    EnforceUniqueValues="FALSE"
    Group="Custom Columns"
    StaticName="RegionalOffice"
    Name="RegionalOffice">
</Field>

Configure the managed metadata column to reference an existing term set

To link the field to a term set so users can select terms you can either add a <Customization/> section to the field and hardcode the various Id’s that define a term set (required if you are using a sandboxed solution), or configure these in code. I’m taking the code approach here as it is the only way to ensure the field will work across multiple environments.

I’ve based the code on Wictor’s excellent example with a couple of minor updates. Instead of hardcoding the name of the term store I’m getting the default keyword store associated with the site (this means we do not have to hardcode the name of the managed metadata service but you should check this works in all your environments). I’ve also added in some additional error handling so we get informative messages if the metadata service and/or term set does not exist.

public class RegionalOfficeEventReceiver : SPFeatureReceiver
{
    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
        SPSite site = properties.Feature.Parent as SPSite;
        ConnectTaxonomyField(site,
            new Guid("{F88D530B-705F-488A-9069-FAF5C79B61F7}"),
            "Global",
            "Office Locations");
    }

 

    public static void ConnectTaxonomyField(SPSite site, Guid fieldId, string termGroup, string termSetName)
    {
        if (site.RootWeb.Fields.Contains(fieldId))
        {
            TaxonomySession session = new TaxonomySession(site);

 

            if (session.DefaultKeywordsTermStore != null)
            {
                // get the default metadata service application
                var termStore = session.DefaultKeywordsTermStore;
                var group = termStore.Groups.GetByName(termGroup);
                var termSet = group.TermSets.GetByName(termSetName);
                TaxonomyField field = site.RootWeb.Fields[fieldId] as TaxonomyField;
                // connect the field to the specified term
                field.SspId = termSet.TermStore.Id;
                field.TermSetId = termSet.Id;
                field.TargetTemplate = string.Empty;
                field.AnchorId = Guid.Empty;
                field.Update();
            }
            else
            {
                throw new TermStoreNotFoundException(string.Format("DefaultKeywordsTermStore not found in site {0}", site.Url));
            }
        }
        else
        {
            throw new ArgumentException(string.Format("Field {0} not found in site {1}", fieldId, site.Url), "fieldId");
        }
    }
}

 

[Serializable]
public class TermStoreNotFoundException : Exception
{
    public TermStoreNotFoundException() { }
    public TermStoreNotFoundException(string message) : base(message) { }
    public TermStoreNotFoundException(string message, Exception inner) : base(message, inner) { }
    protected TermStoreNotFoundException(
      System.Runtime.Serialization.SerializationInfo info,
      System.Runtime.Serialization.StreamingContext context)
        : base(info, context) { }
}

 

public static class TaxonomyExtensions
{
    public static Group GetByName(this GroupCollection groupCollection, string name)
    {
        if (String.IsNullOrEmpty(name))
        {
            throw new ArgumentException("Taxonomy group name cannot be empty", "name");
        }
        foreach (var group in groupCollection)
        {
            if (group.Name == name)
            {
                return group;
            }
        }
        throw new ArgumentOutOfRangeException("name", name, "Could not find the taxonomy group");
    }

 

    public static TermSet GetByName(this TermSetCollection termSets, string name)
    {
        if (String.IsNullOrEmpty(name))
        {
            throw new ArgumentException("Term set name cannot be empty", "name");
        }
        foreach (var termSet in termSets)
        {
            if (termSet.Name == name)
            {
                return termSet;
            }
        }
        throw new ArgumentOutOfRangeException("name", name, "Could not find the term set");
    }
}

 

Note that if we are only creating a site column (or even a content type) this should work without completing the following steps. When we add either the site column or content type to a list the managed metadata field works and shows up in the search refinement panel (at least it did in my testing). This is due to the fact that SharePoint automatically wires some extra bits (shown below) when the field is added. At least in my testing when we create a list definition this isn’t all wired up correctly and the following steps were required.

Provision a hidden field of type Note

This and the following step is often missed out in other articles but cause problems if you try to create custom list definitions that include the managed metadata column. The specific error message you get is:

Error – Failed to get value of the “{0}” column from the “Managed Metadata” field type control. See details in log. Exception message: Invalid field name. {00000000-0000-0000-0000-000000000000}

sp2010-managed-metadata-field-error

The problem stems from the fact that SharePoint is looking for an associated note (i.e. text) field that is required for the metadata column to work. The first taxonomy field we created actually only stores a lookup value to a list within the current site collection and it is the note field that actually stores the names and Id’s of each selected term. So to create this note field we can add a field similar to the following:

<Field Type="Note"
    DisplayName="Regional Office_0"
    StaticName="RegionalOfficeTaxHTField0"
    Name="RegionalOfficeTaxHTField0"
    ID="{9EDAB26E-CC44-4027-AB05-CB44EA3A6F72}"
    ShowInViewForms="FALSE"
    Required="FALSE"
    Hidden="TRUE"
    CanToggleHidden="TRUE"
    RowOrdinal="0" />

Note: SharePoint uses the suffix _0 appended to the display name of the field and TaxHTField0 after the static name so I’m following those conventions.

Configure the managed metadata column to use the hidden Note field

To link the two together we can update the field we created in step one to include a customization section that points to the note field as shown below:

<Field ID="{F88D530B-705F-488A-9069-FAF5C79B61F7}"
    Type="TaxonomyFieldType"
    DisplayName="Regional Office"
    ShowField="Term1033"
    EnforceUniqueValues="FALSE"
    Group="Custom Columns"
    StaticName="RegionalOffice"
    Name="RegionalOffice">
    <Customization>
        <ArrayOfProperty>
            <Property>
                <Name>TextField</Name>
                <Value xmlns:q6="http://www.w3.org/2001/XMLSchema"
                    p4:type="q6:string"
                    xmlns:p4="http://www.w3.org/2001/XMLSchema-instance">{9EDAB26E-CC44-4027-AB05-CB44EA3A6F72}</Value>
            </Property>
        </ArrayOfProperty>
    </Customization>
</Field>

Note the value within the TextField property is the ID of the note field we created above.

Ensure that the TaxonomyFieldAdded feature is activated on the site collection

This is another step that isn’t always mentioned but if this feature is not present then you are presented with a greyed out metadata selection when adding or editing items (note this also happens if the field is not connected to a term set). You won’t get any obvious error messages and the feature is hidden so you won’t see it in the site features list either.

sp2010-managed-metadata-field-disabled

The TaxonomyFieldAdded feature is not activated on sites created using the blank site template, or sites created using custom site definitions so a good idea is to include a feature activation dependency that ensures this is activated before we attempt to provision our field. This is easy to do using the Visual Studio 2010 feature editor – just expand the Feature Activation Dependencies section at the bottom and add the following information:

Title: TaxonomyFieldAdded

Feature ID: 73ef14b1-13a9-416b-a9b5-ececa2b0604c

Description: Register taxonomy site wide field added event receiver

Assuming the feature that contains the site columns is site scoped this feature will activate automatically if it isn’t already activated.

vs2010-feature-activation-dependency

At this point we have a robust way of deploying a standalone site column which is useful in certain situations but in many situations we need to provision content types, list definitions and list instances which is when things get tricky…

Create a list definition that uses a managed metadata field

As explained in the previous post Issues provisioning SharePoint 2010 Managed Metadata fields – to ensure the metadata field automatically appears in the search refinement panel as it does for all other managed metadata fields we need to add the TaxCatchAll columns to our list definition and wire up two taxonomy event receivers.

Create a content type that uses the managed metadata field

I find the easiest way to ensure all the columns are added to the list definition is to add them to a content type first. This content type should contain the managed metadata/taxonomy field, the note field and the TaxCatchAll and TaxCatchAllLabel fields as shown below.

<!-- Parent ContentType: Document (0x0101) -->
<ContentType ID="0x0101006d47bceb88064a04a63716fe4609647a"
    Name="Regional Document"
    Group="Custom Content Types"
    Description="My Content Type"
    Inherits="TRUE"
    Version="0">
    <FieldRefs>
        <FieldRef ID="{F88D530B-705F-488A-9069-FAF5C79B61F7}" Name="RegionalOffice"/>
        <FieldRef ID="{9EDAB26E-CC44-4027-AB05-CB44EA3A6F72}" Name="RegionalOfficeTaxHTField0"/>
        <FieldRef ID="{f3b0adf9-c1a2-4b02-920d-943fba4b3611}" Name="TaxCatchAll"/>
        <FieldRef ID="{8f6b6dd8-9357-4019-8172-966fcd502ed2}" Name="TaxCatchAllLabel"/>
    </FieldRefs>
</ContentType>

Create a list definition that uses the content type and includes all metadata and TaxCatchAll columns

We can do this first step using the Visual Studio 2010 tools to create a list definition from the content type we created earlier. As we added the TaxCatchAll columns to our content type these will automatically be added to our list definition.

Sidenote: you want to remove the /Lists/ prefix from the URL when creating document libraries to avoid breaking the default column values feature. You will also want to add in the file dialog view as it goes missing when using the Visual Studio 2010 tools.

Attach the TaxonomyItemSynchronousAddedEventReceiver and TaxonomyItemUpdatingEventReceiver event receivers to the list definition

If we inspect a list that contains a managed metadata field created through the UI it shows two event receivers. These are the TaxonomyItemSynchronousAddedEventReceiver for the ItemAdding event and the TaxonomyItemUpdatingEventReceiver for the ItemUpdating event. To add these to our custom list definition we use an element file similar to the one below (note the ListTemplateId should match the Type attribute of our list definition).

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Receivers ListTemplateId="10700">
      <Receiver>
        <Name>TaxonomyItemSynchronousAddedEventReceiver</Name>
        <Type>ItemAdding</Type>
        <Assembly>Microsoft.SharePoint.Taxonomy, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
        <Class>Microsoft.SharePoint.Taxonomy.TaxonomyItemEventReceiver</Class>
        <SequenceNumber>10000</SequenceNumber>
      </Receiver>
      <Receiver>
        <Name>TaxonomyItemUpdatingEventReceiver</Name>
        <Type>ItemUpdating</Type>
        <Assembly>Microsoft.SharePoint.Taxonomy, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
        <Class>Microsoft.SharePoint.Taxonomy.TaxonomyItemEventReceiver</Class>
        <SequenceNumber>10000</SequenceNumber>
      </Receiver>
  </Receivers>
</Elements>

Create a list instance to provision instances of the list definition

As a final step we can optionally create a list instance so that when we deploy our solution lists are created automatically. This should be done as a separate web scoped feature. All going well we will now have a list that contains a correctly functioning managed metadata field which automatically gets extracted during search processing and displayed in the search refinement panel as shown below.

sp2010-managed-metadata-term-setExample Project

I’ve included the code above in a Visual Studio 2010 project to deploy a managed metadata field as I always find examples work best for this type of thing.

To run the project you will need a machine with Visual Studio 2010, SharePoint Server 2010 and and managed metadata service with a Group named ‘Global’ and a Term Set named ‘Office Locations’ (or you can wire this up to a term set of you choice by changing the code in the feature receiver).

 

I’m sure there are other rough edges that I haven’t covered yet so let me know if you have come across any other odd behaviour when provisioning managed metadata site columns in the comments below.

Post to Twitter Post to Delicious Post to Digg Post to Reddit Post to StumbleUpon

Written by Ari Bakker

March 8th, 2011 at 11:16 am

53 Responses to 'Provisioning SharePoint 2010 Managed Metadata fields'

Subscribe to comments with RSS or TrackBack to 'Provisioning SharePoint 2010 Managed Metadata fields'.

  1. Thank you for a great blogpost!

    I have been struggling with ‘greyed metadata’ field for few hours now. My problem was that metadata column didn’t seem to connect to term set. I checked all the things on this post (and many others) but couldn’t figure out the why… UNTIL!

    I realised that I had all the components (field, content type, list definition and list intance) in the same feature. Event receiver fired of course, but it didn’t update the column that was already in the list instance (since it had been created already, at least I think so).

    Solution was to separate field and content type in one feature (with event receiver) and list instance to another.

    LazyJones

    24 Oct 13 at 10:10 am

  2. […] by Ari Bakker (whose image I’m using above, thanks Ari), Wictor Wilen and Andrew Connell were popular in […]

Leave a Reply

*