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
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
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.
comments powered by Disqus