Stefan Goßner

Senior Escalation Engineer for SharePoint (WSS, SPS, MOSS, SP2010) and MCMS

Deep Dive into the SharePoint Content Deployment and Migration API - Part 3

Deep Dive into the SharePoint Content Deployment and Migration API - Part 3

  • Comments 129
  • Likes

[Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6 - Part 7]

Providing some real world samples for import

After we managed to export content in part 2 lets now focus on the importing side. Import actually has two different ways to work:

  • Import by preserving the object identity and location
  • Import without preserving the object identity

The first method is used in the Content Deployment function available through the Central administration page. The second method is used by STSADM -o import and when doing the Copy/Move operations in Site Manager. The first method is similar to the import method used in CMS 2002: the imported objects will get the same GUIDs as on the source system. The second method on the other hand allows to import items at different places in the site collection hierarchy and can therefore be used for copy operations.

To demonstrate how powerful the Content Deployment and Migration API is in this area I will again provide some real world examples.

  1. Import by preserving the object identity
  2. Change the Parent of the imported objects during Import

The examples below assume that the destination site collection is on the local server on port 2001.


1) Import with preserving the object identity

A pre-requesit of this method is that the parents of all imported objects need to be in the destination database. So it is not possible to use this method to import into a site collection with a different structure. Usually it means you will use this in classic staging scenarios where you have an authoring farm, a staging farm and a production farm. The content is initially created on the authoring farm and then deployed to the staging farm.

For this method it doesn't matter if the package only contains a single list item or a web or a complete site collection. It also doesn't matter if the export has been done using incremental deployment or using full deployment. As all parents for the objects in the package need to be in the destination database the import engine will correctly import the items.

If you have different site collections and need to exchange data between these (e.g. copy a document library from the site collection of http://www.company1.com/ to the site collection of http://www.company2.com/ you need to use the method without preserving the object identity.

SPImportSettings settings = new SPImportSettings();
settings.SiteUrl = "http://localhost:2001";
settings.FileLocation = @"c:\export";
settings.FileCompression = false;
settings.RetainObjectIdentity = true;

SPImport import = new SPImport(settings);

import.Run();  

Similar to the export process we first create an object to configure the import. This is done by using the SPImportSettings method.

Here is a short explanation about the settings I used to perform the import:

  • SiteUrl - this is the Url to the destination site collection the import process will use.
  • FileLocation - this is the directory holding the data that needs to be imported
  • FileCompression - this defines whether the exported data is available in compressed or uncompressed format
  • RetainObjectIdentity - this defines whether to preserve the object identity during import or whether not to preserve the identiy. If the value is true the identity of the objects is preserved. Means Guid and Url of the imported items will be the same as on the exporting server. Be aware that this means that the parent objects of all imported need to be available in the destination database. If the value is false the objects will get a new generated Guid during import and it will be possible to control the parent of the imported root objects during import. We will cover this later.


2) Change the Parent of the imported objects during Import

Let's assume we have exported a list from a source site collection using the steps described in point 3 of part 2. And now we would like to import the list into a different web in the destination site collection. It doesn't matter if the destination site collection is the same site collection as the source or if it is in the same database as the source site collection. It also doesn't matter if the destination site collection is in the same or a different farm as the source site collection.

If you export an item from a database (e.g. a list) without exporting it's parent, then the exported item will become orphaned in the package. A package can contain multiple different orphaned objects (e.g. if you export multiple different lists or list items).

The import method allows us to define a new parent for each orphaned object in the export package. Means if you selected web A to be exported and web B where web B is a sub web of web A then you can only change the parent of web A. The parent of web B will always be web A as web B is not orphaned in the export package.

There are actually two methods that can be used to assign a new parent to orphaned objects:

Method 1 - all orphaned objects should be imported into the same sub web

This method will only work if all items need to be added to the same sub web. E.g. if you have exported a couple of lists and document libraries (even from different source webs) and now would like to import them into the same sub web.

SPImportSettings settings = new SPImportSettings();
settings.SiteUrl = "http://localhost:2001";
settings.WebUrl = "http://localhost:2001/MyDestWeb";
settings.FileLocation = @"c:\export";
settings.FileCompression = false;
settings.RetainObjectIdentity = false;

SPImport import = new SPImport(settings);
import.Run();

There are two important settings that are required to get this working:

  • WebUrl - this property defines the web that will be used as the new parent for all orphaned objects in the export package. This method (e.g.) cannot be used if the package contains orphaned documents as a web cannot be the parent of a document. The parent of a document needs to be a list or a folder.
  • RetainObjectIdentity - this defines whether to preserve the object identity during import or whether not to preserve the identity. If the value is false the objects will get a new generated Guid during import and it will be possible to assign a new parent to the imported orphaned objects.

Method 2 - assign an individual parent to each orphaned object

This method is more flexible than Method 1 but also requires a little bit more coding. Here it will be required to intercept the import process to assign a new parent to each orphaned object after the import process has gathered the list of orphaned objects from the import package.

This can be done using a custom event handler:

static void OnImportStarted(object sender, SPDeploymentEventArgs args)
{
   SPSite site = new SPSite("http://localhost:2001");
   SPWeb web = site.RootWeb;

   SPImportObjectCollection rootObjects = args.RootObjects;
   foreach (SPImportObject io in rootObjects)
   {
      io.TargetParentUrl = web.Url;
   } 

   web.dispose();
   site.dispose();
}  

The event handler above does such a "re-parenting" of all orphaned objects which are provided in the RootObjects collection of the event arguments. In the sample above we do actually the same as in the sample provided in Method 1: we assign a fixed web as the new parent of all objects. But you can easily extend the logic in this event handler to (e.g.) assign a different parent based on the object type:

static void OnImportStarted(object sender, SPDeploymentEventArgs args)
{
   SPSite site = new SPSite("http://localhost:2001");
   SPWeb web = site.RootWeb;
   SPList list = web.Lists["MyDocLib"];

   SPImportObjectCollection rootObjects = args.RootObjects;
   foreach (SPImportObject io in rootObjects)
   { 
      if (io.Type == SPDeploymentObjectType.ListItem)               
      {                                                             
         io.TargetParentUrl = list.RootFolder.ServerRelativeUrl;    
      }                                                             
      if (io.Type == SPDeploymentObjectType.List)                   
      {                                                             
         io.TargetParentUrl = web.Url;                              
      }                                                             
      ...                                                           

   }
 
   web.dispose();
   site.dispose();
}  

As you can see the eventhandler can be extended pretty easily. Beside looking on the Object type you could also look at the original TargetParentUrl to see where the source was located and include this in your "routing" logic. Now we need to see how we can hookup this event handler with the import process. This can be done as follows:

static void ImportDocLibItem()
{
   SPImportSettings settings = new SPImportSettings();
   settings.SiteUrl = "http://localhost:2001";
   settings.FileLocation = @"c:\deployment5";
   settings.FileCompression = false;
   settings.RetainObjectIdentity = false;

   SPImport import = new SPImport(settings);

   EventHandler<SPDeploymentEventArgs> eventHandler = new EventHandler<SPDeploymentEventArgs>(OnImportStarted);
   import.Started += eventHandler;

   import.Run();
}

As you can see you need to register the event handler with the import class before starting the import process.

There are a couple more event handlers that can be registered during import which are explained in more details on MSDN:
http://msdn2.microsoft.com/en-us/library/microsoft.sharepoint.deployment.spimport_events.aspx

Similar events can also be registered for export if required:
http://msdn2.microsoft.com/en-us/library/microsoft.sharepoint.deployment.spexport_events.aspx


3) Importing using compression

When importing using compression additional settings in the SPImportSettings object need to be adjusted:

SPImportSettings settings = new SPImportSettings();
settings.FileLocation = @"c:\export";
settings.FileCompression = true;
settings.BaseFileName = "ExportedItems.cmp";
... 

Here is an explanation about the two properties:

  • FileCompression - this property defines whether the content is available as a CAB file or in uncompressed format.
  • FileLocation - this property defines where the export files can be found. If compression is used it defines the location of the compressed content migration pack.
  • BaseFileName - this property is only used if compression is enabled. It defines the name of the compressed content migration pack file. The default extension (if not specified) is ".cmp".

In Part 4 I will cover specific scenarios like adjusting links in move scenarios. E.g. if an image is moved to a new location you would need to adjust the links in all pages that reference this image.

Comments
  • PingBack from http://msdnrss.thecoderblogs.com/2007/08/31/sharepoint-content-deployment-and-the-migration-api/

  • Stefan is back! J Stefan is in Redmond this week, and we had a chance to catch up! In fact, I had dinner

  • Stefan Goßner taucht in einer vierteiligen Serie in die Tiefen der SharePoint Deployment und Migration

  • Fantastic, incredibly detailed, content on content deployment by Stefan

  • [via Stefan Gossner ] This past week I was in Redmond teaching my WCM401 development class in an open

  • Body: Great series of posts by Stefan on how to use the Content Migration API. Very timely for me as

  • 今天凌晨加班的时候偶然翻到 Stefan Gossner 的这几篇文章,强烈推荐给大家: Deep Dive into the SharePoint Content Deployment and Migration

  • Hi.. this is an incredible article for demonstrating migration. However I may sound silly but could you advise me whether migration can be done on different environments like  production to pre-production and vise a versa.

  • Hi Purushotam,

    I'm not sure if I understand what you mean. Migration is usually moving content from a different system into sharepoint.

    Moving content between different SharePoint farms is referred to as deployment.

    The API I showed above can do both.

    If you need further info, please provide more details about what you are looking for.

    Cheers,

    Stefan

  • Great set of articles by Stefan Goßner : Deep Dive Into the SharePoint Content Deployment...

  • MOSS / SharePoint Content Deployment

  • hello , i want to import a item from a list source to another list of another site with web service ... Can you help me ???

  • Hi Issou,

    you need to create a webservice based on the code parts I presented in Part 2 and Part 3 of this article series.

    Cheers,

    Stefan

  • So if you&#39;ve read the earlier posts about the tool ( Introducing the SharePoint Content Deployment

  • Hi,

    Thanks for the great posts. I'm able to successfully export and Import a Web. Everything carried over fine except for the listviewwebparts. It doesn't retain the selected View and the toolbar type. Can you please tell me if this is the expected behavior or I'm missing something?

    Thanks in advance

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
Raw Html Fix