/// <exception cref="ArgumentNullException" /> /// <exception cref="InvalidOperationException" /> private void ProcessArticlesTree(Article newArticle, Article existingArticle, Content definition) { if (newArticle == null || !_filter.Matches(newArticle)) { return; } if (!_filter.Matches(existingArticle)) { existingArticle = null; } if (definition == null) { throw new ArgumentNullException(nameof(definition)); } ValidateDates(newArticle, existingArticle); ArticleData newArticleUpdateData = new ArticleData { ContentId = definition.ContentId, Id = newArticle.Id }; List <int> plainFieldIds = definition.Fields .Where(x => x is PlainField) .Select(x => x.FieldId) .ToList(); if (definition.LoadAllPlainFields) { plainFieldIds.AddRange( _fieldService.List(definition.ContentId) .Where(x => x.RelationType == RelationType.None && definition.Fields.All(y => y.FieldId != x.Id)) .Select(x => x.Id)); } // TODO: исключаем Readonly поля var updatedFields = newArticle.Fields.Values .OfType <PlainArticleField>() .Where(x => plainFieldIds.Contains(x.FieldId.Value) && (existingArticle == null || existingArticle.Fields.Values .OfType <PlainArticleField>() .All(y => y.FieldId != x.FieldId || !HasEqualNativeValues(x, y)))) .Select(x => new FieldData { Id = x.FieldId.Value, Value = x.Value }); newArticleUpdateData.Fields.AddRange(updatedFields); var associationFieldsInfo = ( from fieldDef in definition.Fields.OfType <Association>() join field in newArticle.Fields.Values on fieldDef.FieldId equals field.FieldId select new { field, oldField = existingArticle?.Fields.Values.SingleOrDefault(x => x.FieldId == field.FieldId), fieldDef }).ToArray(); foreach (var fieldToSyncInfo in associationFieldsInfo) { if (fieldToSyncInfo.fieldDef is BackwardRelationField backwardRelationFieldDef) { BackwardArticleField oldField = (BackwardArticleField)fieldToSyncInfo.oldField; BackwardArticleField field = (BackwardArticleField)fieldToSyncInfo.field; int[] idsToAdd = field.GetArticles(_filter) .Where(x => oldField == null || oldField.GetArticles(_filter).All(y => y.Id != x.Id)) .Select(x => x.Id) .ToArray(); int[] idsToRemove = oldField?.GetArticles(_filter) .Select(x => x.Id) .Where(x => field.GetArticles(_filter).All(y => y.Id != x)) .ToArray() ?? new int[0]; if (idsToAdd.Any()) { _updateData.AddRange(idsToAdd.Select(x => new ArticleData { Id = x, ContentId = backwardRelationFieldDef.Content.ContentId, Fields = new List <FieldData> { new FieldData { Id = field.FieldId.Value, ArticleIds = new[] { newArticle.Id } } } })); } if (idsToRemove.Any()) { if (backwardRelationFieldDef.DeletingMode == DeletingMode.Delete) { foreach (int idToRemove in idsToRemove) { _articlesToDelete[idToRemove] = backwardRelationFieldDef.Content; } } else { _updateData.AddRange(idsToRemove.Select(x => new ArticleData { Id = x, ContentId = backwardRelationFieldDef.Content.ContentId })); } } } else if (fieldToSyncInfo.fieldDef is ExtensionField extensionFieldDef) { ExtensionArticleField oldField = (ExtensionArticleField)fieldToSyncInfo.oldField; ExtensionArticleField field = (ExtensionArticleField)fieldToSyncInfo.field; if (oldField == null || field.Value != oldField.Value) { newArticleUpdateData.Fields.Add(new FieldData { Id = extensionFieldDef.FieldId, Value = field.Value, ArticleIds = field.Item == null ? null : new[] { field.Item.Id } }); if (oldField?.Item != null) { _articlesToDelete[oldField.Item.Id] = extensionFieldDef .ContentMapping[oldField.Item.ContentId]; } } } else if (fieldToSyncInfo.field is SingleArticleField) { SingleArticleField oldField = (SingleArticleField)fieldToSyncInfo.oldField; SingleArticleField field = (SingleArticleField)fieldToSyncInfo.field; EntityField entityFieldDef = (EntityField)fieldToSyncInfo.fieldDef; Article item = field.GetItem(_filter); Article oldItem = oldField?.GetItem(_filter); if (item?.Id != oldItem?.Id) { newArticleUpdateData.Fields.Add(new FieldData { Id = field.FieldId.Value, ArticleIds = item == null ? null : new[] { item.Id } }); if (oldItem != null && entityFieldDef.DeletingMode == DeletingMode.Delete) { _articlesToDelete[oldItem.Id] = entityFieldDef.Content; } } } else if (fieldToSyncInfo.field is MultiArticleField) { MultiArticleField oldField = (MultiArticleField)fieldToSyncInfo.oldField; MultiArticleField field = (MultiArticleField)fieldToSyncInfo.field; EntityField entityFieldDef = (EntityField)fieldToSyncInfo.fieldDef; var items = field.GetArticles(_filter).ToArray(); var oldItems = oldField?.GetArticles(_filter).ToArray(); if (items.Length != (oldItems?.Length ?? 0) || items.Any(x => oldItems.All(y => y.Id != x.Id))) { newArticleUpdateData.Fields.Add(new FieldData { Id = field.FieldId.Value, ArticleIds = items.Select(x => x.Id).ToArray() }); if (entityFieldDef.DeletingMode == DeletingMode.Delete) { int[] idsToRemove = oldItems? .Where(x => items.All(y => y.Id != x.Id)) .Select(x => x.Id) .ToArray() ?? new int[0]; foreach (int idToRemove in idsToRemove) { _articlesToDelete[idToRemove] = entityFieldDef.Content; } } } } } if (newArticleUpdateData.Fields.Any()) { _updateData.Add(newArticleUpdateData); } foreach (var fieldInfo in associationFieldsInfo .Where(x => x.fieldDef.UpdatingMode == UpdatingMode.Update || x.fieldDef is ExtensionField)) { Article[] oldFieldsArticles = fieldInfo.oldField == null ? new Article[0] : GetChildArticles(fieldInfo.oldField, _filter).ToArray(); foreach (Article childArticle in GetChildArticles(fieldInfo.field, _filter)) { Content childArticleDef = fieldInfo.fieldDef.GetContents() .SingleOrDefault(x => x.ContentId == childArticle.ContentId); if (childArticleDef == null) { throw new InvalidOperationException($@"There is an conflict in product definition field {fieldInfo.field.FieldId} between ContentId={childArticle.ContentId} and Articleid={childArticle.Id}"); } Article oldChildArticle = oldFieldsArticles.SingleOrDefault(x => x.Id == childArticle.Id); ProcessArticlesTree(childArticle, oldChildArticle, childArticleDef); } } }