Потребовалось пройти по всей иерархии объектов SharePoint, можно это было сделать через рекурсивный проход по SPWeb , и циклы по SPList и SPListItem, но мне показалось это весьма сложным, не очень интересным и весьма медленным. Кроме того, такой подход, делает крайне сложной реализацию поиска изменении.
SharePoint 2007 и 2010, содержат специальный
Content Migration API, который умеет экспортировать данные из SharePoint, на файловую систему, и может инкрементально доставать изменения.
К сожалению этот API не позволяет вместо экспорта объектов на файловую систему, сделать с объектами что то свое. Покопавшись немного в
Reflector я пришел к вот такой реализации энумерации объектов используя этот самый API.
static private string exportData(string srcRootUrl, string exportFileLocation, string exportChangeToken)
{
SPExportObject eobj = new SPExportObject();
eobj.Url = srcRootUrl;
eobj.Type = SPDeploymentObjectType.Web;
var expset = new SPExportSettings();
expset.ExportMethod = string.IsNullOrEmpty(exportChangeToken) ? SPExportMethodType.ExportAll : SPExportMethodType.ExportChanges;
expset.SiteUrl = srcRootUrl;
expset.FileLocation = exportFileLocation;
expset.OverwriteExistingDataFile = true;
expset.FileCompression = false;
expset.AutoGenerateDataFileName = false;
if (!string.IsNullOrEmpty(exportChangeToken))
expset.ExportChangeToken = exportChangeToken;
expset.ExportObjects.Add(eobj);
var export = new SPExport(expset);
typeof(SPExport).InvokeMember("InitializeExport", BindingFlags.Default | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
null, export, null);
Console.WriteLine("Change token: {0}", export.Settings.CurrentChangeToken);
var lastChangeToken = export.Settings.CurrentChangeToken;
typeof(SPExport).InvokeMember("CalculateObjectsToExport", BindingFlags.Default | BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.NonPublic,
null, export, null);
PropertyInfo ObjectTotalProp = typeof(SPExport).GetProperty("ObjectsTotal", BindingFlags.Default | BindingFlags.Instance | BindingFlags.NonPublic);
var total = (int)ObjectTotalProp.GetValue(export, null);
Console.WriteLine("Total objects to migrate: {0}", total);
PropertyInfo ObjectManagerProp = typeof(SPExport).GetProperty("ObjectManager", BindingFlags.Default | BindingFlags.Instance | BindingFlags.NonPublic);
var ObjectManager = (IEnumerable)ObjectManagerProp.GetValue(export, null);
var DeploymentObjectType = typeof(SPExport).Assembly.GetType("Microsoft.SharePoint.Deployment.ExportObject", true);
var DeploymentObjectUrlProp = DeploymentObjectType.GetProperty("Url", BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public);
var DeploymentObjectTypeProp = DeploymentObjectType.GetProperty("Type", BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public);
var DeploymentObjectSPObjectProp = DeploymentObjectType.GetProperty("SPObject", BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public);
var DeploymentObjectObjectInfoProp = DeploymentObjectType.GetProperty("ObjectInfo", BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public);
var DeploymentObjectParentWebIdProp = DeploymentObjectType.GetProperty("ParentWebId", BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public);
var DeploymentObjectIdProp = DeploymentObjectType.GetProperty("Id", BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public);
foreach (object obj in ObjectManager)
{
var url = (string)DeploymentObjectUrlProp.GetValue(obj, null);
var type = (Type)DeploymentObjectTypeProp.GetValue(obj, null);
var spobj = DeploymentObjectSPObjectProp.GetValue(obj, null);
var expobj = (SPExportObject)DeploymentObjectObjectInfoProp.GetValue(obj, null);
var spObjId = (Guid)DeploymentObjectIdProp.GetValue(obj, null);
var parentWebId = (Guid)DeploymentObjectParentWebIdProp.GetValue(obj, null);
Console.WriteLine("--- object with type '{1}' from url='{0}' Id='{2}' parentWebId='{3}' spObject='{4}'", url, type, spObjId, parentWebId, spobj);
// add your code here
}
return lastChangeToken;
}
Использовать можно так - выдаст сначала все объекты, а потом будет отдавать изменения :
var srcRootUrl = @"http://server/MySite";
var exportFileLocation = @"C:\ExportFolder";
string lastChangeToken = "";
while (true)
{
Thread.Sleep(1000);
Console.WriteLine("Export start with token '{0}'", lastChangeToken);
lastChangeToken = exportData(srcRootUrl, exportFileLocation, lastChangeToken);
}
Из артефактов осталась необходимость в фолдере на фаиловой системе куда сохраняется пустой файл "ExportSettings.xml"