Provisioning SharePoint Lookup Columns

There is alot of misinformation out there in the blogosphere regarding SharePoint Lookup Columns and how to provision them correctly with a Feature.

Let me just say that I’ve tried about every technique I have found, including declaritvely defining the lookup column through CAML schema (field and list definition).  Every technique I have tried (declaritvely) doesn’t work. MSDN references that the <Field> tag can have a List property and that it is the “internal name” of the list. Others say it’s the GUID of the list, while others say it has to all be done in the list schema.

So…as a result, let me give you my two cents on the subject. DON’T declaritvely try to provision a Lookup field. It’s more of a headache than its really worth. In my opinion, the best practice is to do it through a Feature Receiver. You will have complete control over how the lookup field will get created and used. Besides, using a list GUID through XML schema is nonsense, unless you are creating a ListInstance with that GUID (but I’ve tried that too and it doesn’t work).

Go the Feature Receiver route, trust me. There are a few posts out there on various blogs, including a downloadable example on CodePlex. They are all great and will work. However, if you don’t want to bother with those…let me just provide you my technique on provisioning a Lookup column.

First, define a feature that will call a custom Feature Receiver like the following:

<?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns=http://schemas.microsoft.com/sharepoint/
	Id="6B7DB96E-851B-4790-9A54-6B79DC61C0A7"
	Title="Base Feature"
	Description="Provisions base content types and fields for the site."
	Scope="Site"
	ReceiverAssembly="TSC.SharePoint, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f1710125c5a87ae6"
	ReceiverClass="TSC.SharePoint.BaseFeatureReceiver"
	Hidden="false"
	ImageUrl="TSC/MyFeature.jpg"
	ImageUrlAltText="TSC Custom Feature">
	<ElementManifests>
		<ElementManifest Location="tscfields.xml"/>
		<ElementManifest Location="tscctypes.xml"/>
	</ElementManifests>
</Feature>

Next, create a class in Visual Studio that will inherit SPFeatureReceiver like the following:

public class TSCBaseFeatureReceiver : SPFeatureReceiver
{
    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
        using (SPSite site = (SPSite)properties.Feature.Parent)
        {
              using (SPWeb web = site.RootWeb)
              {
                 // Set lookup columns.
                 SPFieldLookup lookupCol = ProvisionLookupField("MyFieldTitle", "My Field Display Name", "My Site Columns", true, true, SPContext.Current.Site.RootWeb, SPContext.Current.Site.RootWeb.Lists["My List Title"], "Title");
                 LinkToCType(SPContext.Current.Site.RootWeb, "My Content Type", (SPField)lookupCol);
              }
         }
    }

    public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
    {
    }

    public override void FeatureInstalled(SPFeatureReceiverProperties properties)
    {
    }

    public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
    {
    }
}

The last step is to create the methods the code above calls. You can place these methods in the same Feature Receiver below the “FeatureUninstalling” method.

It is important to have your feature first create the lookup column, then to associate the column to a content type. If you look at my Feature XML, you will see that I have an ElementManifest to provision a “tscctypes.xml” file. Within this file, I have defined a few custom content types. You can follow the same process, just change “My Content Type” to the name of the content type you are defining in your Feature.

Now, here are the remaining pieces of code to get this to work:

public static SPFieldLookup ProvisionLookupField(string fieldName, string fieldDisplayName, string group, bool required, bool allowMultipleValues, SPWeb web, SPList lookupList, string lookupField)
{
    web.Fields.AddLookup(fieldName, lookupList.ID, lookupList.ParentWeb.ID, required);
    web.Update();
    SPFieldLookup lookup = (SPFieldLookup)web.Fields.GetFieldByInternalName(fieldName);
    lookup.AllowMultipleValues = allowMultipleValues;
    lookup.LookupField = lookupField;
    lookup.Title = fieldDisplayName;
    lookup.Group = group;
    lookup.Update(true);
    return lookup;
}

public static void LinkToCType(SPWeb web, string contentType, SPField field)
{
    SPContentType ct = web.ContentTypes[contentType];
    ct.FieldLinks.Add(new SPFieldLink(field));
    ct.Update();
}

That’s it!

Advertisements