In my previous post I wrote about the well-known artefact of sandbox solutions that provisioned files are by default still checked-out. This can be fixed via a FeatureReceiver that checks-in all files and approves the ones that require moderation, so that all provisioned SharePoint artefacts are direct available for the end user.
Initially this worked like a charm, and the problem seemed resolved. However when our project progressed and the number of provisioned files increased, we structurally encountered the following error upon feature activation:
Microsoft.SharePoint.SPException occurred
Message=The file _catalogs/masterpage/ApplicationX/ApplXPageLayoutXX.aspx
is modified by domain\WvStrien on May 11 2011 17:06:12 +0200.
Source=Microsoft.SharePoint
ErrorCode=-2130575305
NativeErrorMessage=FAILED hr detected (hr = 0x81020015)
Message=The file _catalogs/masterpage/ApplicationX/ApplXPageLayoutXX.aspx
is modified by domain\WvStrien on May 11 2011 17:06:12 +0200.
Source=Microsoft.SharePoint
ErrorCode=-2130575305
NativeErrorMessage=FAILED hr detected (hr = 0x81020015)
In the root cause analysis of the problem I discovered that the problem only manifests itself when more than 1 file requires approval. This also explained why initially we did not encounter the problem: we started out with only a single PageLayout. With this insight I did an internet search for '0x81020015'. Although no direct related pointers to our situation, this led me in the direction of the problem cause. Apparently it is a race condition within SharePoint internally when first checking in a file and immediately approving it in the same execution context. The internal SharePoint administration is typically not ready yet to handle the second state change on the file.
If this is the cause, then the solution is to break up the execution context: first do the checkin(), and in another execution context the approve() invocation. Normally you would do this by delegating it to a background thread. However, this is not possible in a Sandbox context: the SharePoint ObjectModel is only supported on the main thread of the SPUserWorker process. The best next and pragmatic approach is then to break the execution flow by introducing a delay between the invocation of the 2 SPFile methods:
private static void PublishFile(SPFile checkedOutFile, bool approvalNeeded)
{
checkedOutFile.CheckIn(String.Empty, SPCheckinType.MajorCheckIn);
if (approvalNeeded)
{
int nrOfWaits = 0;
while (!checkedOutFile.Exists && nrOfWaits++ < 5)Thread.Sleep(1000);
if (checkedOutFile.Exists)
checkedOutFile.Web.GetFile(checkedOutFile.UniqueId).Approve(COMMENT);
}
}
{
checkedOutFile.CheckIn(String.Empty, SPCheckinType.MajorCheckIn);
if (approvalNeeded)
{
int nrOfWaits = 0;
while (!checkedOutFile.Exists && nrOfWaits++ < 5)Thread.Sleep(1000);
if (checkedOutFile.Exists)
checkedOutFile.Web.GetFile(checkedOutFile.UniqueId).Approve(COMMENT);
}
}