Close

May 25, 2013

Incorrect values for User Fields while copying list items from one site collection to another

Scenario

Recently, I was writting a piece of code to copy list item from a list in one site collection to the list in another site collection. I did the shallow copy (copying field by field).
Both the lists were created using same List definition so they had same fields and content types etc.
The item was successfully copied but then I noticed that the values of user fields were not correct.

Here is the pice of code that i used for copying the item


Public static void Main(string[] args)
{
    using (SPSite sourceSite = new SPSite("http://sp2010demo/sites/source"))
    {
      using (SPWeb web = sourceSite .OpenWeb())
      {
          SPList sourceList= web.Lists.TryGetList("Source List");
          //fetch first item - for demonstration purpose
          SPListItem sourceItem = sourceList.Items[0];
          //instantiate destination site collection
          using (SPSite destinationSite = new SPSite("http://sp2010demo/sites/destination/"))
          {
             var destinationList = destinationSite.RootWeb.Lists.TryGetList("DestinationList");
             if (destinationList != null)
             {
                //Add new item
                SPListItem destinationItem = destinationList.Items.Add();
                //copy item
                CopyListItem(sourceItem, destinationItem);
             }
          }
      }
   }
}

public static void CopyListItem(SPListItem sourceItem, SPListItem destinationItem)
{
        foreach (SPField field in sourceItem.Fields)
        { 
          //Filter out readonly and attachment fields
          if ((!field.ReadOnlyField) && (field.InternalName != "Attachments"))
          {
                destinationItem[field.Title] = sourceItem[field.Title];
          }
        } 
        destinationItem.Update();
}

Apparently, it looked just fine and i used the same piece of code to copy the list items from one list to another within the same site collection and it worked just perfect as it should !

Problem

So, the problem was actually that when you move this item to a different site collection, users have to be present there in order for it to have the correct value.

If you look at the internal representation of the user field it is basically a look up value and the value is represented something like this: 1;#Sanjay Bhagia

Where first number is the user id in User Information List in the site collection that can be accessed (only if you are admin) by navigating to the following url in your site _catalogs/users/simple.aspx (e.g., http://sp2010demo/sites/source/_catalogs/users/simple.aspx).

So lets do some investigation.

I first open my source site collection i.e., http://sp2010demo/sites/source and navigate to my user information list by navigating to the following url http://sp2010demo/sites/source_catalogs/users/simple.aspx It shows all the users that have been added in my site

userinfosourcesite

Now, if you put the cursor on the user link, you can see the entire url in the status bar of your browser. At the end of the url you can see the querystring ID that has value 1 in this example.

Now lets navigate to the same user list in destination site collection by (http://sp2010demo/sites/destination/_catalog/users/simple.aspx).

Note: In my environment, I already have my user added in both the site collections, that is why I’m able to see my user at both the places but it is possible that you don’t see any user at both the places. User will be added automatically if you have logged in with that user on that site

userinfodestination

By observing the url of the same user we can see that the user id for this user in destination site collection is 10.

So if I’m copying the list item that has user field and that field has my user added (i.e., Sanjay Bhagia) the representation of that user in my source site collection will be 1;#Sanjay Bhagia but the representation of the same user in my destination site collection will be 10;#Sanjay Bhagia !! Hence, the copied item will not display the proper user at the destination list item.

Solution
It is quite easy to fix this issue, I basically fetched the value from user field and called SPWeb.EnsureUser() method to make sure the user is added in the User Information List in my destination site collection and updated the LookupId attribute of the SPUser object.

Here is the modified code:


public static void CopyListItem(SPListItem sourceItem, SPListItem destinationItem)
{
    foreach (SPField field in sourceItem.Fields)
    {
        //Filter out readonly and attachment fields
	if ((!field.ReadOnlyField) && (field.InternalName != "Attachments"))
        {
            //only for user type fields
            if (field.GetType() == typeof(SPFieldUser))
            {
               SPWeb destinationParentWeb = destinationItem.ParentList.ParentWeb;
               if (sourceItem[field.Title] != null)
               {
                 //for field can have multiple selection enabled otherwise you can user SPFieldUserValue class
                 SPFieldUserValueCollection users = new SPFieldUserValueCollection(sourceItem.ParentList.ParentWeb, sourceItem[field.Title].ToString());
                 foreach (SPFieldUserValue user in users)
                 {
                    //this LookupId is the number of user object in User Information List
                    user.LookupId = destinationParentWeb.EnsureUser(user.User.LoginName).ID;
                 }
                destinationItem[field.Title] = users;
               }
            }
            else
              destinationItem[field.Title] = sourceItem[field.Title];
                    }
        }
    destinationItem.Update();
}

So basically, I’m making sure that user object in destination site collection has the correct look up id, that’s it!

Hope it helps.