Migrating Managed Metadata in SharePoint Server 2010

Summary:  Learn how to use the Managed Metadata Service application to migrate managed metadata among Microsoft SharePoint Server 2010 development, testing, and production environments.

Applies to: Business Connectivity Services | Open XML | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio

Provided by:  Scot Hillier (SharePoint MVP), Technical Solutions, LLC

Contents

  • Introduction to the Managed Metadata Service Application in SharePoint Server 2010

  • Creating and Migrating Taxonomies Using the Term Store Management Tool

  • Creating and Migrating Enterprise Content Types in SharePoint Server 2010

  • Conclusion

  • Additional Resources

Introduction to the Managed Metadata Service Application in SharePoint Server 2010

The Managed Metadata Service (MMS) is one of several service applications that are available in Microsoft SharePoint Server 2010. MMS provides two important services: taxonomy services and content type publishing services. Taxonomy services enable you to create and manage hierarchical collections of centrally managed terms (known as term sets) that you can use as attributes for items in SharePoint sites. You can share term sets across site collections and web applications. Content type publishing services enable you to also share content types across site collections and web applications. MMS is accessible through the Central Administration site, as shown in Figure 1.

Figure 1. Administering the Managed Metadata Service application

Administering the Managed Metadata Service

You can create and manage term sets by using the Term Store Management Tool. Taxonomists, administrators, or other individuals who manage taxonomies can use this tool to create, import, and manage term sets and the terms that they contain. The Term Store Management Tool displays all of the global term sets and any local term sets that are available for the site collection from which you access the tool, as shown in Figure 2.

Figure 2. Term Store Management tool

Term Store Management tool

The farm administrator configures content type publishing by configuring a site collection as a hub for content type publishing. After MMS is created and configured, the farm administrator publishes the MMS. The farm administrator can use the published URL to connect other web applications to this MMS and subscribe to its content types.

Creating and Migrating Taxonomies Using the Term Store Management Tool

Create taxonomies in the Term Store Management Tool, either in the Central Administration site or at the site-collection level. Taxonomies are created as term sets that contain multiple terms. Terms may have additional child terms with up to seven levels of nesting.

SharePoint Server 2010 displays terms from taxonomies in Managed metadata columns. Managed metadata columns are fields whose values are selected from a taxonomy. The Managed metadata column uses a special interface to display, navigate, and select values from a taxonomy (see Figure 3). Taxonomies may be created in global groups or in groups that are specific to a site collection.

Figure 3. Working with a Managed Metadata column

Working with a Managed Metadata column

As with many SharePoint artifacts, taxonomies are often created in a development environment as part of a larger feature. After the initial taxonomy is created, it may need to be migrated to a test, staging, or production environment. When migration is required, the taxonomy must be exported from one environment and imported into another environment. The Term Store Management Tool supports importing taxonomies, but it does not support exporting them. For this reason, you must use custom code to support taxonomy migration.

Managed Metadata Input File Format

Importing taxonomies into the MMS is supported by using a comma-delimited file format (.csv file). This file format has a specific structure that defines a term set and the hierarchy of contained terms. You can view a sample import file by selecting View a Sample Import File in the Term Store Management Tool, as shown in Figure 4.

Figure 4. Sample import file

Sample import file

After a file is created in the correct format, it may be imported into the MMS. The import command is available from the shortcut menu that is associated with any group in the Term Store Management Tool, as shown in Figure 5.

Figure 5. Importing a taxonomy

Importing a taxonomy

In addition to importing taxonomy items through the Term Store Management Tool, developers can use the taxonomy object model to import items. The taxonomy object model is available when a reference is set to the Microsoft.SharePoint.Taxonomy.dll assembly. This assembly is found in the ISAPI folder in the SharePoint Server 2010 system directory. The ImportManager class has an ImportTermSet() method that can be used to import the taxonomy. The following code shows how to programmatically import a .csv file that contains a taxonomy. The code expects three arguments to be passed: The Site Collection URL, the file path to the .csv file that contains terms, and the name of the group where the new term set will be created.

static void Main(string[] args)
{
    try
    {

        if (args.Length != 3)
            throw new Exception(
        "Usage: ImportTermSet SiteCollectionUrl FilePath GroupName");

        string siteCollectionUrl = args[0];
        string filePath = args[1];
        string groupName = args[2];

        StreamReader reader = File.OpenText(filePath);
        using (SPSite (siteCollectionUrl))
        {
            TaxonomySession session =
              new TaxonomySession(siteCollection);
            TermStore store = session.TermStores[0];
            Group group = store.Groups.Where(
              g => g.Name == groupName).FirstOrDefault();
            if (group == null)
                group = store.CreateGroup(groupName);
            ImportManager manager = store.GetImportManager();
            bool allTermsAdded = false;
            string errorMessage = string.Empty;
            manager.ImportTermSet(group, reader,
                    out allTermsAdded, out errorMessage);

            if (errorMessage.Length > 0)
                throw new Exception(errorMessage);
        }

        Console.WriteLine("Import complete!");
    }
    catch (Exception x)
    {
        Console.WriteLine(x.Message);
    }
}

Exporting Taxonomies from SharePoint Server 2010

The challenge with migrating taxonomies is that no export functionality is exposed through the Term Store Management Tool. Therefore, exporting must be done entirely through custom code. In the taxonomy object model, the TermSet class exposes an Export() method; however, this method is not implemented. As a result, custom code must be written to enumerate the various terms is the TermSet object and to write to the required .csv file. The following code shows a complete console application for exporting taxonomies into a .csv file. The code expects two arguments to be passed: the Site Collection URL and the label that identifies the term set to export.

static void Main(string[] args)
{
  try
  {

    if (args.Length != 2)
      throw new Exception(
        "Usage: ExportTermSet SiteCollectionUrl RequiredTermLabel");

    string siteCollectionUrl = args[0];
    string requiredTermLabel = args[1];

    using (SPSite siteCollection = new SPSite(args[0]))
    {
      TaxonomySession session = new TaxonomySession(siteCollection);
      string[] requiredTermLabels = { requiredTermLabel };
      TermSetCollection termSets =
        session.GetTermSets(requiredTermLabels);

        if (termSets.Count > 0)
        {
          foreach (TermSet termSet in termSets)
          {
              // termSet.Export(); is NOT IMPLEMENTED. 

              StreamWriter writer =
                File.CreateText(termSet.Name + ".csv");
              writer.AutoFlush = true;
              List<string> headerNameData = new List<string>()
              {
                  "\"Term Set Name\"",
                  "\"Term Set Description\"",
                  "\"LCID\"",
                  "\"Available for Tagging\"",
                  "\"Term Description\"",
                  "\"Level 1 Term\"",
                  "\"Level 2 Term\"",
                  "\"Level 3 Term\"",
                  "\"Level 4 Term\"",
                  "\"Level 5 Term\"",
                  "\"Level 6 Term\"",
                  "\"Level 7 Term\""
              };
              List<string> headerValueData = new List<string>()
              {
                  termSet.Name,
                  termSet.Description,
                  null,
                  termSet.IsAvailableForTagging.ToString(),
                  null,null,null,null,null,null,null,null
              };

              writer.WriteLine(headerNameData.GetCSVFormat());
              writer.WriteLine(headerValueData.GetCSVFormat());
              foreach (Term level1Term in termSet.Terms)
              {
                  List<string> nameData =
                    new List<string>() { level1Term.Name };
                  List<string> termData = new List<string>(){
                      null,null,null,
                      level1Term.IsAvailableForTagging.ToString(),
                      level1Term.GetDescription(),
                      nameData.GetCSVFormat(),
                      null,null,null,null,null,null};

                  writer.WriteLine(termData.GetCSVFormat());

                  WriteSubTerms(level1Term, nameData, writer);

              }

              writer.Close();
          }
      }
      else
      {
          throw new Exception(
            "No term set named'" + requiredTermLabel + "'found.");
      }
  }

    Console.WriteLine("Export complete!");
  }
  catch (Exception x)
  {
    Console.WriteLine(x.Message);
  }
}

static void WriteSubTerms(Term ParentTerm, List<string> ParentNameData, StreamWriter Writer)
{
  foreach (Term levelTerm in ParentTerm.Terms)
  {
    List<string> nameData = new List<string>();
    nameData.AddRange(ParentNameData);
    nameData.Add(levelTerm.Name);

    List<string> termData = new List<string>(){
                 null,
                 null,
                 null,
                 levelTerm.IsAvailableForTagging.ToString(),
                 levelTerm.GetDescription(),
                 nameData.GetCSVFormat()};
    for (int i = 0; i < 7 - ParentNameData.Count; i++)
    {
        termData.Add(null);
    }

    Writer.WriteLine(termData.GetCSVFormat());

    WriteSubTerms(levelTerm, nameData, Writer);

  }
}

Migrating Managed Metadata Fields

As part of the development process involving taxonomies, new lists may be created that use managed metadata fields. Like their associated taxonomies, these lists may also need to be migrated from the development environment to a testing, staging, or production environment. Unfortunately, migrating such lists poses problems with globally unique identifiers (GUIDs).

MMS assigns GUIDs as IDs to TermSet objects and the Term objects that they contain. SharePoint Server 2010 assigns GUID values when the terms are created or imported. If a taxonomy is exported from one environment and imported into another, the system assigns new GUID values. When a managed metadata column is used with a list, the list uses the GUID values to identify the associated term set and selected terms. If such a list instance is migrated to a new environment where the taxonomy GUID values have changed, the column ceases to function and must be recreated in the new environment.

Creating and Migrating Enterprise Content Types in SharePoint Server 2010

Enterprise content types are content types that are specifically assigned to a content type hub for use within subscribing site collections. Enterprise content types may be created directly in the browser within the content type gallery of the hub, or they may be created as features that are installed in the hub.

For developers who are interested in creating enterprise content types that can be migrated between environments, the correct approach is to create them as features by using Microsoft Visual Studio 2010. The idea is to create content types that are packaged within SharePoint solution package (.wsp) files, so that they may be moved easily from the hub in one environment to the hub in another. However, developers must create these features so that they work correctly with content type hubs.

As with many SharePoint projects, content types may be created either declaratively or programmatically. To create content types declaratively, create an Elements.xml file that specifies the columns and forms to be used. The following example shows a content type being defined declaratively.

<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Field
    ID="{D713DCD2-6626-47a1-A23F-3C24CD66A3F9}" 
    Name="Amount"
    DisplayName="Amount"
    Type="Currency"
    Decimals="2"
    Min="0" 
    Group="Financial Columns">
  </Field>
  <ContentType
    ID="0x01010012841A8869DB425cB829C3875EC558CE"
    Name="Financial Document"
    Description="Base financial document"
    Version="0"
    Group="Financial Document Types" >
    <FieldRefs>
      <FieldRef 
        ID="{D713DCD2-6626-47a1-A23F-3C24CD66A3F9}" 
        Name="Amount" DisplayName="Amount" />
    </FieldRefs>
  </ContentType>
</Elements>

The problem with declarative content types is that they do not work correctly in content type hubs. Ideally, you would install the feature that contains the content types into the hub and then publish them to the subscribing site collections. However, this operation fails if the content types are created declaratively. The workaround is to create the content types programmatically. The following code shows how to use a feature receiver to create the enterprise content type programmatically.

SPSite siteCollection = (SPSite)properties.Feature.Parent;
SPWeb site = siteCollection.OpenWeb();

/* CREATE SITE COLUMN */

//The amount.
string amountFieldName = site.Fields.Add("Amount", SPFieldType.Currency, false);
SPFieldCurrency amountField = (SPFieldCurrency)site.Fields.GetFieldByInternalName(amountFieldName);
amountField.Group = "Financial Columns";
amountField.Update();

/* CREATE CONTENT TYPE */

//Get the parent document content type.
SPContentTypeId documentCTypeId = new SPContentTypeId(documentId);
SPContentType documentCType = site.AvailableContentTypes[documentCTypeId];

//Get the financial document content type.
SPContentType financialDocumentCType = new SPContentType(documentCType, site.ContentTypes, "Financial Document");
financialDocumentCType.Group = "Financial Content Types";
site.ContentTypes.Add(financialDocumentCType);
financialDocumentCType = site.ContentTypes[financialDocumentCType.Id];

/* ADD COLUMN TO CONTENT TYPE */

//The amount column.
SPFieldLink amountLink = new SPFieldLink(amountField);
financialDocumentCType.FieldLinks.Add(amountLink);
financialDocumentCType.Update(true);

Conclusion

The Managed Metadata Service application in SharePoint Server 2010 provides capabilities to support enterprise content management. When creating solutions that use the capabilities of the Managed Metadata Service application, consider what will happen to metadata when you migrate between environments. SharePoint Server 2010 presents some limitations that affect taxonomies and enterprise content types, but sufficient workarounds exist to enable SharePoint Server 2010 solutions to participate in a rigorous software development life cycle.

Additional Resources

For more information, see the following resources: