Exception on custom ASP.Net page hosting SharePoint FormField controls

Working with one of my customers and they were wanting to setup an email notification on a custom site that was originally based on a blog site.  The notification needed to be sent whenever someone created/modified a post or a comment associated with the post, and the alert needed to be based on the Categories associated with the Post.  For example, there is a category named "IT" and users wanted to be notified anytime someone creates or modifies a post that has the "IT" category associated with it.  We created a simple sequential workflow to handle the logic of sending out the notifications; however, the problem came in when we modified their custom new and edit pages.  These were ASP.Net pages leveraging the SharePoint Form Field controls.  These pages originally did not display the Category field as they were not being used.  Since they wanted multiple categories for each item, we setup the out of the box Category field on the Post list to be Multi-select and then included a MultipleLookupField control to the page, and hooked up the control in the code behind, everything's working great.

We move the code and pages to the test server and they work until we hookup the workflow.  Then we get this exception:

Exception type: System.InvalidCastException
Message: Unable to cast object of type 'Microsoft.SharePoint.SPFieldWorkflowStatus' to type 'Microsoft.SharePoint.SPFieldLookup'.

That's odd, the page isn't hooking anything up to WorkflowStatus, so we unhook the workflow and the page starts loading.  Add the workflow back and the page still works.  IISReset and the page is still loading.  Thinking everything is done, we call it a night. 

Next day, we find out the exception is back.  To see what's going on I get a copy of the pages and setup a repro.  Luckily the issue reproduced on my dev box, so I attached windbg.exe [from the Debugging Tools for Windows install] to the app pool and run the following command to break on managed exceptions:

sxe clr

I then reproduce the issue and windbg breaks on the managed exception.  I ran !clrstack to confirm it's the stack from the ASPX Error page.  This confirms that SharePoint is looping through the controls on the page and hooking up the SharePoint column to the field controls that are specified, but based on the exception, when it's trying to load our MultipleLookupField control, it's trying to hook up the Workflow Status instead of the Category column:

0:020> !clrstack
OS Thread Id: 0x1450 (20)
03f5e8a8 7793fbae [HelperMethodFrame_1OBJ: 03f5e8a8]
03f5e908 0b6b3820 Microsoft.SharePoint.WebControls.LookupField.CreateChildControls()
03f5ea18 0b6b306e Microsoft.SharePoint.WebControls.MultipleLookupField.CreateChildControls()
03f5ea3c 6d4fd2f8 System.Web.UI.Control.EnsureChildControls()
03f5ea68 0b14bfb1 Microsoft.SharePoint.WebControls.BaseFieldControl.OnLoad(System.EventArgs)
03f5ecb0 6d4fc2a3 System.Web.UI.Control.LoadRecursive()
03f5ecc8 6d4fc2fe System.Web.UI.Control.LoadRecursive()
03f5ece0 6d4fc2fe System.Web.UI.Control.LoadRecursive()
03f5ecf8 6d4fc2fe System.Web.UI.Control.LoadRecursive()
03f5ed10 6d4fc2fe System.Web.UI.Control.LoadRecursive()
03f5ed28 6d4fc2fe System.Web.UI.Control.LoadRecursive()
03f5ed40 6d4fc2fe System.Web.UI.Control.LoadRecursive()
03f5ed58 6d4f8354 System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)
03f5eeb0 6d4f7f84 System.Web.UI.Page.ProcessRequest(Boolean, Boolean)
03f5eee8 6d4f7eb1 System.Web.UI.Page.ProcessRequest()
03f5ef20 6d4f7e46 System.Web.UI.Page.ProcessRequestWithNoAssert(System.Web.HttpContext)
03f5ef2c 6d4f7e22 System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)


From here I took a look at the objects on the stack to try and figure out why SharePoint is linking the MultipleLookupColumn to Workflow Status.  You do this by running !dumpstackobjects [!dso] on the thread that has the exception on it.  This will dump out a list of objects associated with the stack:

0:020> !dso
OS Thread Id: 0x1450 (20)
ESP/REG Object Name
03f5e7d8 0251439c System.InvalidCastException
03f5e820 0251439c System.InvalidCastException
03f5e868 024a4dac Microsoft.SharePoint.WebControls.MultipleLookupField
03f5e948 024db9cc Microsoft.SharePoint.SPFieldCollection

I can see the SPFieldCollection object which is the XML Schema of the fields associated with the list, so I dump this out using !dumpobject [!do]. 

0:020> !do 024db9cc
Name: Microsoft.SharePoint.SPFieldCollection
MethodTable: 03f0e3ac
EEClass: 077f49e0
Size: 84(0x54) bytes
GC Generation: 0
MT Field Offset Type VT Attr Value Name
07922af0 40010be 4 ...SPPersistedObject 0 instance 00000000 m_ContainingObject
71a72da0 400007a 10 System.Int32 1 instance 0 m_cDirtyUpdates
0969d670 400007b 8 ...rtyUpdateDelegate 0 instance 00000000 OnAddDelayDirtyUpdate
0969d754 400007c c ...rtyUpdateDelegate 0 instance 00000000 OnRemoveDelayDirtyUpdate
71a72da0 400007d 14 System.Int32 1 instance -1 m_potentialErrorIndex
096b832c 40027b4 18 ...SharePoint.SPList 0 instance 00000000 m_List
07b1dcd4 40027b5 1c ....SharePoint.SPWeb 0 instance 02451528 m_web
71a44620 40027b6 4c System.Boolean 1 instance 1 m_bAll
71a72da0 40027b7 48 System.Int32 1 instance 6 m_iCount
00000000 40027b8 20 ARRAY 0 instance 024dba20 m_arrFieldSchema
71a44620 40027b9 4d System.Boolean 1 instance 1 m_arrFieldSchemaInited
71a44324 40027ba 24 System.Object[] 0 instance 024dbce0 m_arrFieldSchemaFull
71a44620 40027bb 4e System.Boolean 1 instance 1 m_arrFieldSchemaFullInited
71a70b54 40027bc 28 System.String 0 instance 024da8cc m_strExtendedFieldsXml
71a731a8 40027bd 2c ...ections.Hashtable 0 instance 024e2c38 m_htIdx2Fld
71a731a8 40027be 30 ...ections.Hashtable 0 instance 024e615c m_InternalNameDict
71a731a8 40027bf 34 ...ections.Hashtable 0 instance 024e626c m_InternalNameWithPrefixDict
71a731a8 40027c0 38 ...ections.Hashtable 0 instance 024ee088 m_DisplayNameDict
71a731a8 40027c1 3c ...ections.Hashtable 0 instance 00000000 m_IdDict
71a70b54 40027c2 40 System.String 0 instance 00000000 m_strScope
00000000 40027c3 44 0 instance 00000000 m_Scopes
00000000 40027c4 b80 0 shared static s_SPFieldCreatorDelegates
>> Domain:Value 01236970:NotInit 01739ae0:01b81e2c <<
71a6a35c 40027c5 b84 ....ReaderWriterLock 0 shared static s_fieldMutex
>> Domain:Value 01236970:NotInit 01739ae0:01b81e54 <<

From here, the  m_strExtendedFieldsXml property contains the data we're after, so we dump this out.  You could also dump out the other Schema properties to get one field at a time, but this property will dump out all the Schema as one string so it's easier to get:

0:020> !do 024da8cc
Name: System.String
MethodTable: 71a70b54
EEClass: 7182d65c
Size: 4352(0x1100) bytes
GC Generation: 0
String: <Fields><Field ID="{c042a256-787d-4a6f-8a8a-cf6ab767f12d}" ...<Field></Fields>
MT Field Offset Type VT Attr Value Name
71a72da0 4000096 4 System.Int32 1 instance 2168 m_arrayLength
71a72da0 4000097 8 System.Int32 1 instance 2167 m_stringLength
71a71834 4000098 c System.Char 1 instance 3c m_firstChar
71a70b54 4000099 10 System.String 0 shared static Empty
>> Domain:Value 01236970:01ac1198 01739ae0:01ac1198 <<
71a71784 400009a 14 System.Char[] 0 shared static WhitespaceChars
>> Domain:Value 01236970:01ac16f0 01739ae0:01ac6508 <<

The highlighed String property above contains all the XML schema that defines all the fields associated with the list.  I truncated the above view as it was 5-10 lines long on the page.  I then copied out the XML from the String property, create a new XML document in SharePoint Designer 2007 and paste in the info from the dump.  I can then right-click the page and select "Reformat XML" to clean up what I'm looking at.  I then search for the text "Category" and find the problem.  When the workflow was hooked up, the Workflow Status field has a StaticName and Name value of "Category".  I then find the field with a DisplayName of Category that we're trying to hook up to, and find it has a static name of "PostCategory".  

Workflow Status field that our control is picking up (some properties removed to keep the lines short):
 <Field DisplayName="CategoryAlert" Type="WorkflowStatus" ... StaticName="Category" Name="Category" ... >

Category field that we're trying to hookup (some properties removed to keep the lines short):
 <Field DisplayName="Category" Type="LookupMulti" ... StaticName="PostCategory" Name="PostCategory" ... />

SharePoint will pick up either the Display or Static name of a column, and in this case it was hitting the Workflow Status field first when enumerating controls and picking up the StaticName property before it was picking up the field with a DisplayName of Category.  We update our MultipleLookupField to point to the static name of our column to ensure there are no questions about which field we want, PostCategory, instead of the display name of Category and everything is happy.  Here's what the change looked like:

From:    <SharePoint:MultipleLookupField runat="server" ID="mlfCategory" ControlMode="New" FieldName="Category">

To:   <SharePoint:MultipleLookupField runat="server" ID="mlfCategory" ControlMode="New" FieldName="PostCategory">

You can get the Static and Display Name for fields from teh browser:

  1. Browse the list
  2. Click Settings, then List Settings
  3. Click a Column

The Display Name will show as the Column Name
The Static Name will display in the URL QueryString after Field =