Copy a Field’s Value to another Field in SharePoint

If one of your field data type needs to be changed, you will run into issues when deploying the new version of your application, as SharePoint can’t translate from one data type to another. For example, if you change a field type from “Single Line of Text” to “Notes” in Fields.xml, everything will go wrong.

The clean way to do this is to create a new field, the one that will be used from now on, and copy the value of the old field to the new field, while applying a transformation if necessary.

For this, we wrote a generic method that will go trough the entire site and perform the changes on the specified fields.

public static void CopyFieldValues(SPSite site,
    String copyFromFieldInternalName,
    String copyToFieldInternalName,
    Func<Object, Object> transformation)
{
    foreach (SPWeb web in site.AllWebs)
    {
        //Get all the ListItems in every list of the web that has the 2 fields
        var items = (from l in web.Lists.Cast<SPList>()
                     where l.Fields.ContainsField(copyFromFieldInternalName)
                         && l.Fields.ContainsField(copyToFieldInternalName)
                     from i in l.Items.Cast<SPListItem>()
                     select i).ToList();

        //For each listitem in this web, migrate the content of the old one to the new one
        //To do this, we use the tranformation Func that was given as a parameter
        foreach (SPListItem item in items)
        {
            item[copyToFieldInternalName] = transformation(item[copyFromFieldInternalName]);

            //Do system update to avoid modifying the item's version
            item.SystemUpdate(false);
        }
    }
}

So, now that we have a generic method to copy the content of a field into another, we can “specialize” it for various uses.

One example is migrating a Single Line of Text type field to a Note type field. It is very easy because there is no transformation needed:

public static void CopyFieldValuesFromSingleLineOfTextToNote(SPSite site,
    String copyFromFieldInternalName,
    String copyToFieldInternalName)
{
    //No need to transform the data in this case
    SharePointFieldsHelper.CopyFieldValues(site, copyFromFieldInternalName, copyToFieldInternalName, oldFieldValue => oldFieldValue);
}

More on IsSPBuiltInField…

Some follow up on my previous post

I had to work with this a bit more today, so I wrapped it up a bit to make it more convenient to work with:

public static class SPFieldExtension
{
    static IDictionary<Guid, bool> PublishingBuildInFields { get; set; }

    static SPFieldExtension()
    {
        Type type = typeof(FieldId);

        var propInfoArray = type.GetProperties(BindingFlags.Static | BindingFlags.Public);

        SPFieldExtension.PublishingBuildInFields = propInfoArray
            .Select(prop => (Guid) prop.GetValue(null, null))
            .ToDictionary(g => g, g => true);
    }

    /// <summary>
    /// Check if the given field is a build in field of SharePoint (WSS and MOSS)
    /// </summary>
    /// <param name="guid"></param>
    public static bool IsSPBuiltInField(Guid guid)
    {
        return SPBuiltInFieldId.Contains(guid)
            || SPFieldExtension.PublishingBuildInFields.ContainsKey(guid);
    }

    /// <summary>
    /// Check if the given field is a build in field of SharePoint (WSS and MOSS)
    /// </summary>
    /// <param name="field"></param>
    public static bool IsSPBuiltInField(this SPField field)
    {
        return SPFieldExtension.IsSPBuiltInField(field.Id);
    }
}

So, I created a static class to hold my two methods. One is the same a the one in the previous post, taking a Guid and returning a Boolean. The second one is an extension method for SPField that simply call the first method. It’s more continent to use it this way.