SharePoint: How to Delete a List Field/Column programmatically
Sometimes I need to remove a list field, which is not in use anymore. To remove the field we need just to call SPField.Delete method. However, there are situations when the field cannot be deleted due to some conditions, for example, when the field is read-only and etc. Because of that you might get such exceptions as
"The field cannot be deleted because it is a read only field in the list." "The field cannot be deleted because it is a sealed field in the list." "The field cannot be deleted because it is a hidden field in the list."
and other. So, let’s consider how these difficulties can be overcome (if it’s possible at all).
Inside the SPField.Delete method
The SPField.Delete does nothing except calling the Delete method of the SPFieldCollection class. The listing below demonstrates the short version of the SPFieldCollection.Delete:
public void Delete(string internalFieldName) { SPField fld = ... // get the field from the current collection ... if (!fld.CanBeDeleted) ... // throw an eception SPFieldLookup lookup = fld as SPFieldLookup; if (((lookup != null) && !lookup.IsDependentLookup) && (lookup.GetDependentLookupInternalNames().ToArray().Length != 0)) ... // throw an eception ... // delete the field }
Where the CanBeDeleted property of the field defined as the following:
public bool CanBeDeleted { get { if (this.AllowDeletion.HasValue) return this.AllowDeletion.Value; return (!this.FromBaseType && !this.Sealed); } }
*Note: this code is true for both SharePoint 2007 and SharePoint 2010.
As we can see, deleting a list field, SharePoint explicitly analyzes such properties of the field as AllowDeletion, Sealed and FromBaseType. Additionally, experiments show that the ReadOnlyField and Hidden properties are being examined as well (likely it happens somewhere in the unmanaged SharePoint modules).
Workaround
An obvious workaround is, before calling SPField.Delete, change the above properties so that the field would be allowed for deletion. So, taking that into account, I implemented the following method(s) to delete list fields:
public static bool RemoveField(SPField spField) { if (spField == null) { WriteErrorToLog("spField is null! Please, provide a valid one"); return false; } bool res = false; try { // check if it's a ReadOnly field. // if so, reset it if (spField.ReadOnlyField) { spField.ReadOnlyField = false; spField.Update(); } // check if it's a Hidden field. // if so, reset it if (spField.Hidden) { spField.Hidden = false; spField.Update(); } // check if the AllowDeletion property is set to false. // if so, reset it to true if (spField.AllowDeletion == null || !spField.AllowDeletion.Value) { spField.AllowDeletion = true; spField.Update(); } // If the AllowDeletion property is set, // the Sealed property seems not to be examined at all. // So the following piece of code is commented. /*if(spField.Sealed) { spField.Sealed = false; spField.Update(); }*/ // If the AllowDeletion property is set, // the FromBaseType property seems not to be examined at all. // So the following piece of code is commented. /*if(spField.FromBaseType) { spField.FromBaseType = false; spField.Update(); }*/ // finally, remove the field spField.Delete(); spField.ParentList.Update(); res = true; } catch (Exception ex) { WriteErrorToLog(ex.Message); } return res; } public static bool RemoveField(SPList spList, string displayNameOrInternalNameOrStaticName) { SPField spField = GetFieldByName(spList, displayNameOrInternalNameOrStaticName); if(spField == null) { WriteErrorToLog(string.Format("Couldn't find field {0}!", displayNameOrInternalNameOrStaticName)); return false; } return RemoveField(spField); } public static void WriteErrorToLog(string errorMsg) { // write error into log }
*Note: the GetFieldByName method is described here – Getting SPField with no exceptions to be thrown.
According to the code of the CanBeDeleted, the Sealed and FromBaseType properties are not being examined at all while the AllowDeletion is set. Thus the dealing with them is commented, but retained just in case.
Below is a sample of use:
SPSecurity.RunWithElevatedPrivileges(delegate { using (SPSite spSite = new SPSite("some site url")) using (SPWeb spWeb = spSite.OpenWeb()) { SPList spList = GetListByUrl(spWeb, "Lists/Products"); RemoveField(spList, "product name"); } });
*Note: the GetListByUrl method is described here – Getting SPList with no exceptions to be thrown.
The RemoveField method isn’t a panacea. For example, you still will have problems if the field going to be deleted is a part of a multiple column lookup.
Be very careful when deleting fields, because it can lead to severe issues in your SharePoint applications. If you are uncertain, don’t delete anything, especially if it concerns a live application on a production server. Remember that you are always able to just make the field hidden instead.