Как распространять обновления для всех элементов в графе элементов Colectica DDI?

Недавно мне нужно было обновить более ста элементов Concept, используя скрипт, использующий Colectica SDK для DDI, и мне нужно распространять эти обновления по всем местам в наборе с известным корневым элементом Project. Это означает, что каждый элемент, ссылающийся на более старую версию Концепции, должен быть обновлен для ссылки на новую версию. Это создаст новую версию элемента refrencing, который будет нуждаться в том, чтобы его обновления распространялись в наборе аналогичным образом.

Мое лучшее решение использует реализацию 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 работает очень хорошо, производительность этого посетителя довольно низкая из-за многочисленных обращений в репозиторий Colectica от BeginVisitItem(), Это заставляет меня чувствовать, что я могу поступать неправильно. Есть ли лучший способ приблизиться к этому, используя инструменты, доступные через Colectica SDK?

Я также должен отметить, что мне известна возможность заполнять последние версии всех дочерних элементов, но с этого момента проблема обнаружения всех грязных элементов, повышения их версий, сохранения их и распространения обновлений, похоже, превращается в ту же проблему, что Я уже пытаюсь решить.

Я также хотел бы добавить, что побочный эффект обновления всего, что находится в корневом каталоге, до последних версий, а не только этих концепций и связанных элементов, будет вполне приемлемым и, фактически, может оказаться предпочтительным.

1 ответ

Решение

Вот решение, которое я в итоге придумала.

Начните с получения корневого элемента проекта, используя 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;
                    }
                }
            }
        }
    }
}

Это не так эффективно, как хотелось бы, но работает за один проход, поэтому производительность приемлема.

Другие вопросы по тегам