Deep Dive into the SharePoint Content Deployment and Migration API - Part 3
[Part 1 - Part 2 - Part 3 - Part 4 - Part 5 - Part 6]
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:
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.
-
Import by preserving the object identity
-
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:
SPExportSettings settings = new SPExportSettings();
settings.FileLocation = @"c:\export";
settings.FileCompression = true;
settings.BaseFileName = "ExportedItems.cmp";
Here is an explanation about the two properties:
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.