понедельник, 9 августа 2010 г.

SharePoint: How to use Content migration API for indexing contents

Потребовалось пройти по всей иерархии объектов 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"

Комментариев нет: