C# 如何将更新传播到Colectica DDI项目图中的所有项目?

C# 如何将更新传播到Colectica DDI项目图中的所有项目?,c#,graph,metadata,visitor-pattern,C#,Graph,Metadata,Visitor Pattern,最近,我需要使用一个使用SDK的脚本更新100多个概念项,并且我需要使用一个已知的根项目项将这些更新传播到集合中的所有位置。这意味着引用旧版本概念的每个项目都需要更新以引用新版本。这将创建引用项的新版本,该版本需要在集合内以类似方式传播其更新 我的最佳解决方案是使用IVersionableVisitor的实现来更新过期的引用,并根据需要将项目标记为脏项。此时,可以使用DirtyItemGatherervisitor来收集和发布项目。这不会在一次通过中起作用,但希望它可以运行,直到不再需要更新更多

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

我的最佳解决方案是使用
IVersionableVisitor
的实现来更新过期的引用,并根据需要将项目标记为脏项。此时,可以使用
DirtyItemGatherer
visitor来收集和发布项目。这不会在一次通过中起作用,但希望它可以运行,直到不再需要更新更多的项目

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);
        }
    }
}
内部类引用VersionUpdateVisitor:IVersionableVisitor
{
公共引用版本UpdaterVisitor(WcfRepositoryClient repositoryClient)
{
this.RepositoryClient=RepositoryClient;
this.VisitedLog=新字典();
this.Context=newlist();
}
专用WcfRepositoryClient RepositoryClient{get;set;}
私有字典访问日志{get;set;}
已访问过的公共图书馆(识别者三方id)
{
var logId=Tuple.Create(id.Identifier,id.AgencyId);
返回this.VisitedLog.ContainsKey(logId)和this.VisitedLog[logId];
}
公共无效注册表项(标识符三项id)
{
this.VisitedLog[Tuple.Create(id.Identifier,id.AgencyId)]=true;
}
私有列表上下文{get;set;}
公共无效BeginVisitItem(可交付项)
{
如果(!this.HaveVisited(item.CompositeId)&&
!this.Context.Any(contextItem=>contextItem.CompositeId.Identifier.Equals(item.CompositeId.Identifier)&&contextItem.CompositeId.AgencyId.Equals(item.CompositeId.AgencyId)))
{
如果(!item.IsPopulated)
{
var previousVersion=项目。版本;
this.RepositoryClient.PopulateItem(item,true,ChildReferenceProcessing.Instantiate);
如果(以前的版本!=item.Version)
{
item.IsDirty=真;
}
}
this.Context.Add(项);
}
}
公共无效EndVisitItem(可交付项)
{
如果(!this.HaveVisited(item.CompositeId))
{
此.Context.Remove(项);
这个.RegisterVisit(item.CompositeId);
}
}
}
不幸的是,它不起作用;每次传递都会找到相同的项,因为集合仍然包含引用子项的旧版本的父项。我已经考虑并努力调整这种方法,例如在
EndVisitItem()
中修改项目,以便在升级过程中进行修改,但它并没有解决真正的问题

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

在Colectica Designer中,这个问题是通过内部使用Navigator结构来解决的,不幸的是,我在Colectica SDK中看不到Navigator结构。此外,尽管Colectica Designer基于Navigator的解决方案性能非常好,但由于从
BeginVisitItem()
多次往返到Colectica存储库,因此该访问者的性能相当差。这让我觉得我可能在这件事上做错了是否有更好的方法使用Colectica SDK提供的工具实现此目的?

我还应该注意到,我知道可以在所有子项上填充最新的内容,但是从那时起,查找所有脏项、碰撞它们的版本、保存它们以及传播更新的问题似乎变成了我已经尝试解决的问题


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

以下是我最终提出的解决方案

首先使用
instantialTest
选项获取根项目项

var testProject = repositoryClient.GetLatestItem(
    itemGuid,
    agencyId,
    ChildReferenceProcessing.InstantiateLatest);
接下来,向根项目发送一个
MarkUpdatedReferencesDirtyVisitor
(此访问者的代码包含在回答的末尾)

接下来,发送一个
dirtyitemcolgather
visitor,该visitor不具有收集非脏项的选项,但具有标记为脏项的脏父项的选项

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;
                    }
                }
            }
        }
    }
}
内部类MarkUpdatedReferencesDirtyVisitor:VersionableVisitorBase
{
公共标记UpdatedReferencesDirtyVisitor(
WcfRepositoryClient repositoryClient)
{
this.RepositoryClient=RepositoryClient;
}
专用WcfRepositoryClient RepositoryClient{get;set;}
公共覆盖无效BeginVisitItem(可传递项)
{
如果(!this.HaveVisited(item.CompositeId)&&!item.IsDirty)
{
base.BeginVisitItem(项目);
如果(!item.IsPopulate)
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;
                    }
                }
            }
        }
    }
}