To get us going on the new corporate blog, here is a repost of something I wrote a while back concerning the addition of SPLookup fields to SharePoint lists.
Here is the scenario:
I’ve got a WSP that creates some lists and some of those lists have lookup and multi lookup fields in them that reference other lists in the WSP.
So to start with I configured the fields in the list definition xml like the documentation says you should. The result, they appeared on the list but any attempt to access them programmatically failed with a variety of generic sharepoint errors. When you tried to access an item through the standard admin pages for the list the field would appear but would not link to the source list.
OK, great, so I don’t have time to figure out why this doesn’t work properly so I decide I will just include some code in the already existing feature event listener to add the fields on feature activation.
So I start with the Microsoft documented route:
sourceList.Fields.AddLookup("SomeField", targetList.ID, false);
Excellent, the lookups appear and link up correctly, I’m starting to feel much happier about my impending deadline.
Now I just need to deal with the multi value lookups, so I just extend what is already working to use some of the methods that exist on the object I’ve just created, right? Wrong!
sourceList.Fields.AddLookup(
"SomeField", targetList.ID,
false);
SPFieldLookup lookupField = (SPFieldLookup)sourceList.Fields["SomeField"];
lookupField.AllowMultipleValues = true;
lookupField.Update();
That should work, it compiles with no warnings so all good. Except that if you try and call SPFieldLookup.Update() after modifying any of it’s properties you get the infamous “Cannot complete this action” error and none of your changes are saved.
After knocking about with this for a short while trying to identify which specific action was causing the error and whether there was some way I could work around it, I came up with this alternative.
private static void AddLookupField(string fieldName, string sourceField, SPList sourceList, SPList targetList, bool isMulti, bool isRequired)
{
fieldName = fieldName.Replace("//", "_x002f_");
if (!targetList.Fields.ContainsField(fieldName))
{
string fieldType = isMulti ? "LookupMulti" : "Lookup";
string mult = isMulti ? "TRUE" : "FALSE";
string required = isRequired ? "TRUE" : "FALSE";
string xml = "<Field Type=\"" + fieldType + "\" Mult=\"" + mult + "\" Required=\"" + required + "\" ShowField=\"" + sourceField + "\" DisplayName=\""+ fieldName + "\" StaticName=\"" + fieldName + "\" Name=\"" + fieldName + "\" List=\"" + sourceList.ID.ToString() + "\" />";
targetList.Fields.AddFieldAsXml(xml);
}
}
This function basically takes all the parameters you need to supply to create an SPFieldLookup of just about any type you may need and creates the correct xml fragment for it.
In the words of Ron Burgandy, “60% of the time, it works every time”