2

我最近需要使用Colectica SDK for DDI的脚本更新一百多个概念项目,并且我需要将这些更新传播到具有已知根项目项目的集合中的所有位置。这意味着每个引用旧版本概念的项目都需要更新以引用新版本。这将创建一个新版本的引用项,该版本需要类似地在集合内传播其更新。

我尝试过的最佳解决方案使用 的实现IVersionableVisitor来更新过时的引用,根据需要将项目标记为脏。此时,DirtyItemGatherer可以使用访问者来收集和发布项目。这不会一次性完成,但希望它可以运行直到不再需要更新项目。

internal class ReferencedVersionUpdaterVisitor : IVersionableVisitor
{
    public ReferencedVersionUpdaterVisitor(WcfRepositoryClient repositoryClient)
    {
        this.RepositoryClient = repositoryClient;
        this.VisitedLog = new Dictionary<Tuple<Guid, string>,bool>();
        this.Context = new List<IVersionable>();
    }

    private WcfRepositoryClient RepositoryClient { get; set; }

    private Dictionary<Tuple<Guid, string>, bool> VisitedLog { get; set; }

    public bool HaveVisited(IdentifierTriple id)
    {
        var logId = Tuple.Create(id.Identifier, id.AgencyId);
        return this.VisitedLog.ContainsKey(logId) && this.VisitedLog[logId];
    }

    public void RegisterVisit(IdentifierTriple id)
    {
        this.VisitedLog[Tuple.Create(id.Identifier, id.AgencyId)] = true;
    }

    private List<IVersionable> Context { get; set; }

    public void BeginVisitItem(IVersionable item)
    {
        if (!this.HaveVisited(item.CompositeId) &&
            !this.Context.Any(contextItem => contextItem.CompositeId.Identifier.Equals(item.CompositeId.Identifier) && contextItem.CompositeId.AgencyId.Equals(item.CompositeId.AgencyId)))
        {
            if (!item.IsPopulated)
            {
                var previousVersion = item.Version;
                this.RepositoryClient.PopulateItem(item, true, ChildReferenceProcessing.Instantiate);
                if (previousVersion != item.Version)
                {
                    item.IsDirty = true;
                }
            }
            this.Context.Add(item);
        }
    }

    public void EndVisitItem(IVersionable item)
    {
        if (!this.HaveVisited(item.CompositeId))
        {
            this.Context.Remove(item);
            this.RegisterVisit(item.CompositeId);
        }
    }
}

不幸的是,它不起作用;每次通过都会找到相同的项目,因为该集合仍然包含引用旧版本子项目的父项目。我已经将思想和精力投入到调整这种方法的想法中,例如修改项目EndVisitItem()以在向上修改的过程中进行修改,但这并没有解决真正的问题。

我认为基本问题是访问者在遍历集合时需要修改父项。由于这是一个图表,因此实际上并没有父项,而是我碰巧用来访问当前节点的引用项。我尝试使用该Context属性记录此内容,但其内容似乎并不总是符合我的预期,即上下文列表中的最后一项是“父”引用项。

在 Colectica Designer 中,通过内部使用 Navigator 结构解决了这个问题,遗憾的是我在 Colectica SDK 中看不到它。此外,虽然 Colectica Designer 的基于 Navigator 的解决方案性能非常好,但由于多次往返于BeginVisitItem(). 这让我觉得好像我可能做错了。有没有更好的方法使用 Colectica SDK 提供的工具来解决这个问题?

我还应该注意,我知道在所有子项目上填充最新的能力,但是从那里,定位所有脏项目、碰撞它们的版本、保存它们和传播更新的问题似乎变成了同样的问题我已经在尝试解决了。

我还想补充一点,将根集中的所有内容更新到最新版本,而不仅仅是那些概念和相关项目,这将是非常好的,事实上,可能是首选。

4

1 回答 1

2

这是我最终想出的解决方案。

首先使用该InstantiateLatest选项获取根项目项。

var testProject = repositoryClient.GetLatestItem(
    itemGuid,
    agencyId,
    ChildReferenceProcessing.InstantiateLatest);

接下来向MarkUpdatedReferencesDirtyVisitor根项目发送一个(此访问者的代码包含在此答案的末尾)。

var markUpdatedReferencesDirtyVisitor =
    new MarkUpdatedReferencesDirtyVisitor(repositoryClient);
testProject.Accept(markUpdatedReferencesDirtyVisitor);

通过发送一个DirtyItemGatherer访问者来遵循这一点,该访问者没有收集非脏物品的选项,但可以选择将其标记为脏物品的脏父母。

var dirtyItemGatherer = new DirtyItemGatherer(false, true);
testProject.Accept(dirtyItemGatherer);

在这一点上,在调用WcfRepositoryClient.RegisterItems()发布更新之前,需要做一些基本的样板文件,例如为脏项目增加版本号和进行完整性检查。

这是MarkUpdatedReferencesDirtyVisitor. 基本思想是获取每个项目的子项的最新版本,并将它们与当前的子版本进行比较。如果有差异,请将项目标记为脏。稍后,将发布具有最新孩子的版本。

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;
                    }
                }
            }
        }
    }
}

这不像我希望的那样高效,但它确实在一次通过中起作用,因此性能是可以接受的。

于 2013-10-28T11:30:06.050 回答