Friday, April 23, 2010

SP 2010 Managed Metadata TermSet.CreateTerm Throws Error

I've been working on importing a set of Terms into my Managed Metadata Term Store from a 3rd party database. However, I ran into a snag. When I execute the following code, I get an error, "There is already a term with the same default label and parent term."

public static void CreateTermIfNotExists(TermSet termSet, string termName)
{
 if (termSet != null && !string.IsNullOrEmpty(termName))
 {
  Term term = null;

  //This throws an exception if the Term doesn't exist
  try
  {
   term = termSet.Terms[termName];
  }
  catch { }

  if (term == null)
  {
   Term t = termSet.CreateTerm(termName, 1033);
   termSet.TermStore.CommitAll();
  }
 }
}

I attached my debugger and found out that even though I was checking for my term, the code would say it wasn't there, even though it was! The problem, was that the specific term causing me problems had an ampersand. The term name I supplied was "Foo & Bar", but the value put into the TermStore actually contained unicode version of the ampersand.

Looking through the documentation, I found this relevant comment:

The name value will be normailized to trim consecutive spaces into one and replace the & character with the wide character version of the character (\uFF06). The leading and trailing spaces will be trimmed. It must be non-empty and cannot exceed 255 characters, and cannot contain any of the following characters ; "<>|&tab.

That was indeed the behavior I was seeing. Thinking I had just found a limitation of the TermSet.Terms collection, I changed my code to this:

public static void CreateTermIfNotExists(TermSet termSet, string termName)
{
 if (termSet != null && !string.IsNullOrEmpty(termName))
 {
  Term term = null;

  TermCollection tc = termSet.GetTerms(termName, 1033, true, StringMatchOption.ExactMatch, 1, false);
  if (tc != null && tc.Count > 0)
  {
   term = tc[0];
  }

  if (term == null)
  {
   Term t = termSet.CreateTerm(termName, 1033);
   termSet.TermStore.CommitAll();
  }
 }
}

I ran again, and this time instead of blowing up on just that one case, my code went crazy trying to insert a bunch of different terms that were working fine before and throwing way more exceptions. A little research on this method led me to this post, which suggests the TermSet.GetTerms method does not work in Beta 2, which seems to be what I just discovered as well.

I decided to explore the reference to normalizing term names from the MSDN link. My final pass at the code became:

public static void CreateTermIfNotExists(TermSet termSet, string termName)
{
    if (termSet != null && !string.IsNullOrEmpty(termName))
    {
        Term term = null;

        try
        { 
            string normalizedTermName = TermSet.NormalizeName(termName);
            term = termSet.Terms[normalizedTermName];
        }
        catch { }

        if (term == null)
        {
            Term t = termSet.CreateTerm(termName, 1033);
            termSet.TermStore.CommitAll();
        }
    }
} 

Finally, success! Moral of the story - when you query your TermSet for a specific Term you need to normalize your name first, because that is how it will be stored internally.

2 comments:

  1. Please notice the Deprecated terms. They cannot be reached by termSet.Terms[normalizedTermName];

    ReplyDelete