Here's the solution that I eventually came up with.
Begin by getting root project item using the InstantiateLatest
option.
var testProject = repositoryClient.GetLatestItem(
itemGuid,
agencyId,
ChildReferenceProcessing.InstantiateLatest);
Next send a MarkUpdatedReferencesDirtyVisitor
to the root item (code for this visitor is included at the end of this answer).
var markUpdatedReferencesDirtyVisitor =
new MarkUpdatedReferencesDirtyVisitor(repositoryClient);
testProject.Accept(markUpdatedReferencesDirtyVisitor);
Follow this by sending a DirtyItemGatherer
visitor without the option to gather non-dirty items but with the option to mark as dirty parents of dirty items.
var dirtyItemGatherer = new DirtyItemGatherer(false, true);
testProject.Accept(dirtyItemGatherer);
At this point, it's a matter of doing some basic boilerplate stuff, such as bumping version numbers for dirty items and doing sanity-checks, before making a call to WcfRepositoryClient.RegisterItems()
to publish the updates.
Here is the code for MarkUpdatedReferencesDirtyVisitor
. The basic idea is to grab the latest version of each item's children and compare them to the current child versions. If there is a difference, mark the item as dirty. Later, the version with the latest children will be published.
internal class MarkUpdatedReferencesDirtyVisitor : VersionableVisitorBase
{
public MarkUpdatedReferencesDirtyVisitor(
WcfRepositoryClient repositoryClient)
{
this.RepositoryClient = repositoryClient;
}
private WcfRepositoryClient RepositoryClient { get; set; }
public override void BeginVisitItem(IVersionable item)
{
if (!this.HaveVisited(item.CompositeId) && !item.IsDirty)
{
base.BeginVisitItem(item);
if (!item.IsPopulated)
{
this.RepositoryClient.PopulateItem(
item,
true,
ChildReferenceProcessing.InstantiateLatest);
}
var latestChildren = item.GetChildren();
var currentChildren = this.RepositoryClient.GetItem(
item.CompositeId,
ChildReferenceProcessing.Instantiate).GetChildren();
if (latestChildren.Count != currentChildren.Count)
{
item.IsDirty = true;
}
else
{
for(int i = 0; i < currentChildren.Count; i++)
{
if (!latestChildren[i].CompositeId.Equals(
currentChildren[i].CompositeId))
{
item.IsDirty = true;
break;
}
}
}
}
}
}
This is not as efficient as I would have liked, but it does work in a single pass, so the performance is acceptable.