Provisioning SharePoint Lookup Fields Programmatically

There’s a large number of blog posts floating around on the topic of creating lookup fields (whether programmatically, via XML, or via the UI), as there are many ways of doing so with many variations, each with its own set of limitations. I’ve come up with a reasonably generic and flexible way of doing this with very few real drawbacks. It takes the form of the extension method below which I’ve modified slightly for public consumption.

public static SPFieldCollectionExt
{
  public static SPFieldLookup CreateLookupField(this SPFieldCollection fields, string title, string internalName,
    SPField lookupListTargetField, bool addToAllContentTypes, Action<SPFieldLookup> fieldActions)
  {
    string caml = String.Format(
      "<Field Type=\"Lookup\" DisplayName=\"{0}\" Name=\"{0}\" List=\"{1}\" />",
      internalName, "{" + lookupListTargetField.ParentList.ID.ToString().ToUpper() + "}");

    string nameOfCreatedField = fields.AddFieldAsXml(caml, true,
      addToAllContentTypes ? SPAddFieldOptions.AddToAllContentTypes : SPAddFieldOptions.AddToDefaultContentType);

    return fields.UpdateField<SPFieldLookup>(nameOfCreatedField, f =>
    {
      f.Title = title;
      f.LookupWebId = lookupListTargetField.ParentList.ParentWeb.ID;
      f.LookupField = lookupListTargetField.InternalName;
  
      if (fieldActions != null)
        fieldActions(f);
    });
  }


  static T UpdateField<T>(this SPFieldCollection fields, string fieldInternalName, Action<T> fieldActions) where T : SPField
  {
    var field = (T)fields.GetFieldByInternalName(fieldInternalName);
    fieldActions(field);
    field.Update();
    return (T)fields.GetFieldByInternalName(fieldInternalName);
  }
}

Which can be called using code like this:

site.RootWeb.Lists["Pages"].Fields.CreateLookupField("My Lookup Field", "MyLookupField", lookupTargetField, true, f => 
{
  f.AllowMultipleValues = true;
  f.Required = true;
});

Advantages:

  • This method allows a lookup field to target a list outside the current web. Most methods (particularly CAML oriented ones) don’t as they rely on setting List to a web relative URL rather than a list ID.
  • This method allows any number of field properties to be configured in a strongly typed fashion by way of a lambda (see the final advantage below). And Update() is called automatically afterwards.
  • This method uses AddFieldAsXml() instead of Add() or AddLookup(). This means:
    • You can add a list field to all content types available in that list. Basically, without extra work, the latter two methods cannot be used to achieve what you’d accomplish in the UI by ticking the below box:
      pic
    • It can be used to add custom field types -though this would only matter if you were subclassing SPFieldLookup. More generally speaking, a CreateField() wrapper (one which can create any type of field) should call the first method for this reason.
  • This abstracts away lookup/CAML nastiness. E.g. it’s not sufficient to supply the list ID – despite its uniqueness, one must still provide the LookupWebId. But callers of this method need not worry about such SP idiosyncrasies.
  • It uses a programmatic approach – some will disagree here, but one day I’ll write a blog post which explains why I consider this approach superior. Of course, this is a disadvantage if in your codebase, you do everything via XML.

Disadvantages:

  • The lookup target list must exist prior to calling this. But this is most likely a non-issue – if you’re adding a lookup field programmatically, you’re probably also provisioning the list programmatically… therefore, you have control over when this happens.
  • This method will only add a lookup to existing content types. Content types added after the fact will need to be updated.

About Nathan Pitman

Undergrad software engineer at the University of Auckland
This entry was posted in C#, SharePoint and tagged , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *