Saturday, August 30, 2014

Assign values to managed metadata fields programmatically in sandbox solutions in Sharepoint

Assigning values to managed metadata fields programmatically in farm solution, which have full trust and access to Microsoft.SharePoint.Taxonomy namespace, is well known currently and don’t cause much problems:

   1: var field = (TaxonomyField)item.Fields["Foo"];
   2: var session = new TaxonomySession(site);
   3: var term = session.GetTerm(termId);
   4: field.SetFieldValue(item, term);

In this code we use assumption that term guid is already known (in farm solution it can be retrieved from terms collection by term label).

Also it is possible to set value to managed metadata field via client object model, but instead of Microsoft.SharePoint.Taxonomy assembly we should use Microsoft.SharePoint.Client.Taxonomy assembly:

   1: var ctx = new ClientContext(url);
   2: var list = ctx.Web.Lists.GetByTitle("MyList");
   3: var fields = list.Fields;
   4: var field = fields.GetByTitle("Foo");
   5:  
   6: var query = new CamlQuery();
   7: string queryXml = string.Format(
   8: "<view>" +
   9: "  <query>" +
  10: "    <where>" +
  11: "      <eq>" +
  12: "        <fieldref name=\"ID\" />" +
  13: "        <value type=\"Integer\">{0}</value>" +
  14: "      </eq>" +
  15: "    </where>" +
  16: "  </query>" +
  17: "</view>", id);
  18: query.ViewXml = queryXml;
  19: var listItems = list.GetItems(query);
  20:  
  21: ctx.Load(listItems, items => items.Include(i => i["Foo"]));
  22: ctx.Load(fields);
  23: ctx.Load(field);
  24: ctx.ExecuteQuery();
  25:  
  26: if (listItems.Count != 1)
  27: {
  28:     return;
  29: }
  30: var item = listItems[0];
  31:  
  32: var txField = ctx.CastTo<TaxonomyField>(field);
  33: var termValue = new TaxonomyFieldValue();
  34: termValue.Label = label;
  35: termValue.TermGuid = termId;
  36: termValue.WssId = -1;
  37: txField.SetFieldValueByValue(item, termValue);
  38: item.Update();
  39: ctx.Load(item);
  40: ctx.ExecuteQuery();

In this code we also assume that we know term guid – it is needed for proper creation of TaxonomyFieldValue object (lines 33-36). In case of client object model it is more complicated to get it. But e.g. if you need to copy value of one list item to another, you may get term guid from source item by casting it to string, which looks like this: “{wssId};#{label}|{guid}”.

The remaining question is to how set value of managed metadata field programmatically in sandbox solution? We know that sandbox solutions which contain code are now deprecated, but it is still may be needed in the real life. The problem is that sandbox solutions are partially trusted and don’t have access neither to Microsoft.SharePoint.Taxonomy.dll nor to Microsoft.SharePoint.Client.Taxonomy.dll. During investigations I didn’t find standard ways to do it, and only found one trick (or even hack) which works in sandbox. This trick is based on the fact that for each managed metadata field in Sharepoint there is hidden Note field (see e.g. Problem with not crawled managed metadata fields in Sharepoint 2013 for some details) which also contains string representation of the taxonomy value, but not in the form used in the managed metadata field itself (see above). In the Note field value is stored in the following form: “{label}|{guid}”. And the trick is that if we have source list item with specified value in managed metadata field, we may build string representation of taxonomy value in the form “-1;#{label}|{guid}” (we used “-1” for wssId here, which would mean that term is not in the TaxonomyHiddenList list yet. Even if it exists there I didn’t found problems with this approach – see How to Work with Managed Metadata Columns by Using the SharePoint Client Object Model) and then assign this string directly to managed metadata field. Also for safety it is better to assign Note field value itself:

   1: var val = sourceItem["FooTaxHTField"];
   2: if (val != null)
   3: {
   4:     targetItem["Foo"] = string.Format("-1;#{0}", val);
   5: }
   6: else
   7: {
   8:     targetItem["Foo"] = null;
   9: }
  10: targetItem["FooTaxHTField"] = val;
  11: targetItem.Update();

This approach may be problematic if you don’t have source item from which you need to copy the taxonomy value, because you will need to get term guid somehow and you can’t get it from terms collection in sandbox code. But if you have static structure of the terms you may provision them with known ids and then use these ids in the code. Anyway I hope that this information will be useful for you and will help in your work.

1 comment: