/// <exception cref="NotSupportedException" /> /// <exception cref="InvalidOperationException" /> public string GetEditorJsonSchemaString(Content definition) { JSchema schema = GetSchema( definition, forList: false, includeRegionTags: false, excludeVirtualFields: true); return JsonConvert.SerializeObject(schema); }
/// <exception cref="NotSupportedException" /> /// <exception cref="InvalidOperationException" /> private JSchema GetVirtualFieldSchema( BaseVirtualField baseVirtualField, Content definition, SchemaContext context) { if (baseVirtualField is VirtualMultiEntityField virtualMultiEntityField) { var boundFieldKey = Tuple.Create(definition, virtualMultiEntityField.Path); Field boundField = context.VirtualFields[boundFieldKey]; var contentForArrayFields = ((EntityField)boundField).Content; var itemSchema = new JSchema { Type = JSchemaType.Object }; foreach (BaseVirtualField childField in virtualMultiEntityField.Fields) { JSchema childfieldSchema = GetVirtualFieldSchema(childField, contentForArrayFields, context); itemSchema.Properties[childField.FieldName] = childfieldSchema; } return new JSchema { Type = JSchemaType.Array, Items = { itemSchema } }; } else if (baseVirtualField is VirtualEntityField virtualEntityField) { BaseVirtualField[] fields = virtualEntityField.Fields; var virtualEntityFieldSchema = new JSchema { Type = JSchemaType.Object }; foreach (BaseVirtualField childField in fields) { JSchema childfieldSchema = GetVirtualFieldSchema(childField, definition, context); virtualEntityFieldSchema.Properties[childField.FieldName] = childfieldSchema; } return virtualEntityFieldSchema; } else if (baseVirtualField is VirtualField virtualField) { if (virtualField.Converter != null) { return new JSchema { Type = ConvertTypeToJsType(virtualField.Converter.OutputType) }; } else { var virtualFieldKey = Tuple.Create(definition, virtualField.Path); return GetFieldSchema(context.VirtualFields[virtualFieldKey], null, context, false); } } else { throw new NotSupportedException($"Field type {baseVirtualField.GetType()} is not supported"); } }
public static DefinitionTreeNode[] GetObjectsFromPath(Content rootContent, string path, IFieldService fieldService, DefinitionEditorService definitionEditorService, ContentService contentService) { //запрос корня if (string.IsNullOrEmpty(path)) { return new[] { new DefinitionTreeNode(rootContent, string.Empty, null, false, false, contentService) } } ; bool notFoundInDef; object foundObject = definitionEditorService.GetObjectFromPath(rootContent, path, out notFoundInDef); if (foundObject is Content) { return(GetContentFields((Content)foundObject, fieldService, path, foundObject == rootContent)); } var contentsFromDef = definitionEditorService.GetContentsFromField((Field)foundObject); var nodesFromContentsInDef = contentsFromDef .Select(x => new DefinitionTreeNode(x, path, null, foundObject is Dictionaries, false, contentService)) .ToArray(); if (foundObject is ExtensionField) { var allExtensions = fieldService.ListRelated(fieldService.Read(((Field)foundObject).FieldId).ContentId) .Where(x => x.Aggregated) .Select(x => x.Content) .ToArray(); return(nodesFromContentsInDef .Concat(allExtensions .Where(x => contentsFromDef.All(y => y.ContentId != x.Id)) .Select(x => new DefinitionTreeNode(x, path))) .ToArray()); } else if (foundObject is Dictionaries) { int[] allContentIds = definitionEditorService.GetAllContentIdsFromTree(rootContent); return(nodesFromContentsInDef .Concat(allContentIds .Where(x => contentsFromDef.All(y => y.ContentId != x)) .Select(x => new DefinitionTreeNode(contentService.Read(x), path)) .OrderBy(x => x.text)) .ToArray()); } else { return(nodesFromContentsInDef); } } }
public Article DeserializeProduct(JObject rootArticleDictionary, Content definition) { var product = rootArticleDictionary.SelectToken("product"); if (product != null) { rootArticleDictionary = (JObject)product; } var productDeserializer = ObjectFactoryBase.Resolve<IProductDeserializer>(); return productDeserializer.Deserialize(new JsonProductDataSource(rootArticleDictionary), definition); }
private void FillDefinitionsFields(DefinitionsFields definitionsFields, Content definition, Field[] parentFields, int rootContentId) { if (definition.LoadAllPlainFields) { if (!definitionsFields.ContentIdsDependentOfAllPlainFields.ContainsKey(definition.ContentId)) { definitionsFields.ContentIdsDependentOfAllPlainFields[definition.ContentId] = new List <PathToRootContent>(); } definitionsFields.ContentIdsDependentOfAllPlainFields[definition.ContentId].Add(new PathToRootContent(rootContentId, parentFields)); } foreach (var field in definition.Fields) { if (field is BackwardRelationField) { if (!definitionsFields.MonitoredBackwardRelationFields.ContainsKey(field.FieldId)) { definitionsFields.MonitoredBackwardRelationFields[field.FieldId] = new List <PathToRootContent>(); } definitionsFields.MonitoredBackwardRelationFields[field.FieldId].Add(new PathToRootContent(rootContentId, parentFields)); } else { if (!definitionsFields.MonitoredFieldIds.ContainsKey(field.FieldId)) { definitionsFields.MonitoredFieldIds[field.FieldId] = new List <PathToRootContent>(); } definitionsFields.MonitoredFieldIds[field.FieldId].Add(new PathToRootContent(rootContentId, parentFields)); } var childDefinitionParentFields = new[] { field }.Concat(parentFields).ToArray(); if (field is BackwardRelationField) { FillDefinitionsFields(definitionsFields, ((BackwardRelationField)field).Content, childDefinitionParentFields, rootContentId); } else if (field is EntityField) { FillDefinitionsFields(definitionsFields, ((EntityField)field).Content, childDefinitionParentFields, rootContentId); } else if (field is ExtensionField) { foreach (var extFieldDefinition in ((ExtensionField)field).ContentMapping.Select(x => x.Value)) { FillDefinitionsFields(definitionsFields, extFieldDefinition, childDefinitionParentFields, rootContentId); } } } }
public DefinitionTreeNode(Content content, string parentPath, string ownPath, bool isFromDictionaries, bool notInDefinition, ContentService contentService) { Id = ownPath ?? parentPath + Separator + content.ContentId; text = string.IsNullOrEmpty(content.ContentName) ? contentService.Read(content.ContentId).Name : content.ContentName; expanded = hasChildren = !isFromDictionaries && !notInDefinition; imageUrl = "images/icons/content.gif"; IconName = "document"; NotInDefinition = notInDefinition; }
private JSchema CreateContentSchema(Content content) { var qpContent = _contentService.Read(content.ContentId); var schema = new JSchema { Type = JSchemaType.Object, AllowAdditionalProperties = false }; schema.Description = !IsHtmlWhiteSpace(qpContent.Description) ? qpContent.Description : !IsHtmlWhiteSpace(qpContent.FriendlySingularName) ? qpContent.FriendlySingularName : !IsHtmlWhiteSpace(qpContent.Name) ? qpContent.Name : null; return schema; }
private static DefinitionTreeNode[] GetContentFields(Content content, IFieldService fieldService, string parentPath, bool isRootContent) { var qpFields = fieldService.List(content.ContentId).Concat(fieldService.ListRelated(content.ContentId)).ToArray(); var fieldsNotInDef = qpFields.Where(x => content.Fields.All(y => y.FieldId != x.Id) && (!x.Aggregated || x.ContentId == content.ContentId)); var contentFields = content.Fields .Select(x => new DefinitionTreeNode(x, parentPath, null, qpFields.All(y => y.Id != x.FieldId) && !(x is Dictionaries) && !(x is BaseVirtualField), false)); if (isRootContent && !content.Fields.Any(x => x is Dictionaries)) { contentFields = contentFields.Concat(new[] { new DefinitionTreeNode(new Dictionaries(), parentPath, null, false, true) }); } return(contentFields .Concat(fieldsNotInDef.Select(x => new DefinitionTreeNode(x, parentPath, null, x.RelationType != RelationType.None || !content.LoadAllPlainFields, x.ContentId != content.ContentId, fieldService))) .ToArray()); }
/// <param name="qpField">Can be null</param> /// <exception cref="NotSupportedException" /> /// <exception cref="InvalidOperationException" /> private JSchema GetFieldSchema( Field field, Quantumart.QP8.BLL.Field qpField, SchemaContext context, bool forList) { if (qpField == null && !(field is BaseVirtualField)) { qpField = _fieldService.Read(field.FieldId); } if (field is PlainField) { return GetPlainFieldSchema(qpField); } else if (field is BackwardRelationField backwardRelationalField) { JSchema backwardItemSchema = GetSchemaRecursive(backwardRelationalField.Content, context, forList); return AttachFieldData(qpField, new JSchema { Type = JSchemaType.Array, Items = { backwardItemSchema } }); } else if (field is EntityField entityField) { Content fieldContent = entityField.Content; if (qpField.RelationType == Quantumart.QP8.BLL.RelationType.OneToMany) { return AttachFieldData(qpField, GetSchemaRecursive(fieldContent, context, forList)); } else { JSchema arrayItemSchema = GetSchemaRecursive(fieldContent, context, forList); return AttachFieldData(qpField, new JSchema { Type = JSchemaType.Array, Items = { arrayItemSchema } }); } } else { throw new NotSupportedException($"Field type {field.GetType()} is not supported"); } }
private void ClearExstensions(Content content, int[] exstensionContentIds, List <int> hashCodes) { int hashCode = content.GetHashCode(); if (hashCodes == null) { hashCodes = new List <int> { hashCode }; } else if (hashCodes.Contains(hashCode)) { return; } else { hashCodes.Add(hashCode); } var exstensions = content.Fields.OfType <ExtensionField>(); foreach (var field in exstensions) { if (field.ContentMapping.Values.Any(c => exstensionContentIds.Contains(c.ContentId))) { int[] ids = field.ContentMapping.Where(e => !exstensionContentIds.Contains(e.Value.ContentId)).Select(e => e.Key).ToArray(); foreach (int id in ids) { field.ContentMapping.Remove(id); } } foreach (var c in field.ContentMapping.Values) { ClearExstensions(c, exstensionContentIds, hashCodes); } } foreach (var field in content.Fields.OfType <EntityField>()) { ClearExstensions(field.Content, exstensionContentIds, hashCodes); } }
/// <summary> /// возвращает Content или Field из rootContent по пути /// </summary> /// <param name="rootContent"></param> /// <param name="path">разделенные Separator идшники контентов и полей</param> /// <param name="notFoundInDef"> /// был ли в rootContent найден по этому пути объект, если нет, будет возвращен с дефолтными /// настройками /// </param> /// <returns></returns> public object GetObjectFromPath(Content rootContent, string path, out bool notFoundInDef) { var entityIds = path.Split(new[] { PathSeparator }, StringSplitOptions.RemoveEmptyEntries) .Select(x => { int fieldId; if (int.TryParse(x, out fieldId)) { return((object)fieldId); } else { return(x.Substring(VirtualFieldPrefix.Length)); } }); return(GetObjectFromDef(rootContent, out notFoundInDef, entityIds)); }
public Field GetFieldByPath(string path, Content definition, out bool hasFilter, out Content parent) { var articleData = DPathProcessor.VerifyAndParseExpression(path).ToList(); hasFilter = articleData.Any(ad => ad.FiltersData.Any()); parent = definition; Field currentField = null; foreach (var fieldName in articleData.Select(ad => ad.FieldName)) { if (currentField != null) { var currentEntityField = currentField as EntityField; if (currentEntityField == null) { throw new Exception("Schema generator requires virtual field to relate to EntityField type or its descendant"); } parent = currentEntityField.Content; } var nonVirtualFields = parent.Fields.Where(x => !(x is BaseVirtualField)).ToArray(); if (nonVirtualFields.All(x => x.FieldName != fieldName) && parent.LoadAllPlainFields) { var allPlainFields = _fieldService.List(parent.ContentId).Where(x => x.RelationType == RelationType.None); currentField = new PlainField { FieldId = allPlainFields.Single(x => x.Name == fieldName).Id, FieldName = fieldName }; } else { currentField = nonVirtualFields.Single(x => x.FieldName == fieldName); } } return(currentField); }
public Article DeserializeProduct(string productJson, Content definition) { return DeserializeProduct(JsonConvert.DeserializeObject<JObject>(productJson), definition); }
/// <exception cref="NotSupportedException" /> /// <exception cref="InvalidOperationException" /> private JSchema GetSchema(Content definition, bool forList, bool includeRegionTags, bool excludeVirtualFields) { _contentService.LoadStructureCache(); _fieldService.LoadStructureCache(); VirtualFieldContext virtualFieldContext = _virtualFieldContextService.GetVirtualFieldContext(definition); var context = new SchemaContext { VirtualFields = virtualFieldContext.VirtualFields, IgnoredFields = virtualFieldContext.IgnoredFields, ExcludeVirtualFields = excludeVirtualFields }; JSchema rootSchema = GetSchemaRecursive(definition, context, forList); if (includeRegionTags) { JSchema schemaWithRegionTags = new JSchema { Type = JSchemaType.Object }; schemaWithRegionTags.Properties.Add(ProductProp, rootSchema); JSchema regionTagsSchema = new JSchema { Type = JSchemaType.Array }; JSchema regionTagSchema = new JSchema { Type = JSchemaType.Object }; regionTagSchema.Properties.Add("title", new JSchema { Type = JSchemaType.String }); JSchema valuesSchema = new JSchema { Type = JSchemaType.Array }; JSchema valueSchema = new JSchema { Type = JSchemaType.Object }; valueSchema.Properties.Add("value", new JSchema { Type = JSchemaType.String }); JSchema regionIdsSchema = new JSchema { Type = JSchemaType.Array }; regionIdsSchema.Items.Add(new JSchema { Type = JSchemaType.Integer }); valueSchema.Properties.Add("regionsId", regionIdsSchema); valueSchema.Required.Add("regionsId"); valueSchema.Required.Add("value"); valuesSchema.Items.Add(valueSchema); regionTagSchema.Properties.Add("values", valuesSchema); regionTagSchema.Required.Add("title"); regionTagSchema.Required.Add("values"); regionTagsSchema.Items.Add(regionTagSchema); schemaWithRegionTags.Properties.Add(RegionTagsProp, regionTagsSchema); FillSchemaDefinitions(schemaWithRegionTags, context); DeduplicateJsonSchema(schemaWithRegionTags, context); return schemaWithRegionTags; } FillSchemaDefinitions(rootSchema, context); DeduplicateJsonSchema(rootSchema, context); return rootSchema; }
/// <exception cref="NotSupportedException" /> /// <exception cref="InvalidOperationException" /> public JSchema GetSchema(Content definition, bool forList = false, bool includeRegionTags = false) { return GetSchema(definition, forList, includeRegionTags, excludeVirtualFields: false); }
/// <summary> /// /// </summary> /// <param name="rootContent"></param> /// <param name="notFoundInDef"></param> /// <param name="entityIds">int c id поля для невиртуальных полей и string с именем поля для виртуальных</param> /// <returns></returns> private object GetObjectFromDef(Content rootContent, out bool notFoundInDef, IEnumerable <object> entityIds) { object[] entityIdsToSearch = entityIds .Skip(1) //корень ровно один поэтому первый компонент пути нам не нужен .ToArray(); object currentPositionObj = rootContent; notFoundInDef = false; foreach (object entityId in entityIdsToSearch) { if (currentPositionObj is Content) { var currContent = (Content)currentPositionObj; if (entityId is int) { currentPositionObj = currContent.Fields.SingleOrDefault(x => x.FieldId == (int)entityId); } else { currentPositionObj = currContent.Fields.SingleOrDefault(x => x.FieldName == (string)entityId && x is BaseVirtualField); } if (currentPositionObj == null) { //дурацкая система что Dictionaries это поле с id=0 if (entityId is int && (int)entityId == 0) { notFoundInDef = true; return(new Dictionaries()); } if (entityId is int) { int enitityIdInt = (int)entityId; var qpField = _fieldService.Read(enitityIdInt); if (qpField == null) { notFoundInDef = true; return(null); } if (qpField.RelationType == RelationType.None && !qpField.IsClassifier) { currentPositionObj = new PlainField { FieldId = enitityIdInt, FieldName = qpField.Name }; notFoundInDef = !currContent.LoadAllPlainFields; } else { using (_fieldService.CreateQpConnectionScope()) { if (qpField.ContentId == currContent.ContentId) { if (!qpField.IsClassifier) { currentPositionObj = new EntityField { FieldId = enitityIdInt, FieldName = qpField.Name, CloningMode = CloningMode.UseExisting, Content = new Content { ContentId = qpField.RelateToContentId.Value, ContentName = qpField.RelatedToContent.Name } } } } ; else { currentPositionObj = new ExtensionField { FieldId = qpField.Id, FieldName = qpField.Name, CloningMode = CloningMode.UseExisting }; var classifierContents = _fieldService.ListRelated(qpField.ContentId) .Where(x => x.Aggregated) .Select(x => x.Content); foreach (var classifierContent in classifierContents) { ((ExtensionField)currentPositionObj).ContentMapping.Add( classifierContent.Id, new Content { ContentId = classifierContent.Id, ContentName = classifierContent.Name }); } } else { currentPositionObj = new BackwardRelationField { FieldId = qpField.Id, FieldName = qpField.Name, Content = new Content { ContentId = qpField.ContentId, ContentName = qpField.Content.Name }, CloningMode = CloningMode.UseExisting }; } } notFoundInDef = !qpField.IsClassifier || !currContent.LoadAllPlainFields; } }
/// <summary> /// Рекурсивно обходит <see cref="Content"/>, генерирует корневую схему и заполняет /// <see cref="SchemaContext.SchemasByContent"/> - созданные схемы контентов /// и <see cref="SchemaContext.RepeatedContents"/> - повторяющиеся контенты /// </summary> /// <exception cref="NotSupportedException" /> /// <exception cref="InvalidOperationException" /> private JSchema GetSchemaRecursive(Content definition, SchemaContext context, bool forList) { if (context.SchemasByContent.ContainsKey(definition)) { context.RepeatedContents.Add(definition); return context.SchemasByContent[definition]; } var contentSchema = CreateContentSchema(definition); context.SchemasByContent[definition] = contentSchema; var qpFields = _fieldService.List(definition.ContentId).ToArray(); contentSchema.Properties.Add(IdProp, new JSchema { Type = JSchemaType.Integer, Minimum = 0, ExclusiveMinimum = true }); contentSchema.Required.Add(IdProp); foreach (Field field in definition.Fields .Where(f => !(f is Dictionaries) && (!forList || f is PlainField plainField && plainField.ShowInList))) { if (field.FieldName == null) { throw new InvalidOperationException( $"FieldName is null: {new { field.FieldId, field.FieldName }}"); } if (field is BaseVirtualField baseVirtualField) { if (!context.ExcludeVirtualFields) { contentSchema.Properties[field.FieldName] = GetVirtualFieldSchema( baseVirtualField, definition, context); } } else if (!context.IgnoredFields.Contains(Tuple.Create(definition, field))) { var qpField = field is BackwardRelationField ? _fieldService.Read(field.FieldId) : qpFields.SingleOrDefault(x => x.Id == field.FieldId); if (qpField == null) { throw new InvalidOperationException($@"There is a field id={ field.FieldId} which specified in product definition and missing in the content id={definition.ContentId}"); } if (field is ExtensionField extensionField) { MergeExtensionFieldSchema(extensionField, qpField, contentSchema, context); } else { contentSchema.Properties[field.FieldName] = GetFieldSchema(field, qpField, context, forList); } } } if (definition.LoadAllPlainFields && !forList) { var fieldsToAdd = qpFields .Where(f => f.RelationType == Quantumart.QP8.BLL.RelationType.None && definition.Fields.All(y => y.FieldId != f.Id)) .Where(f => !context.IgnoredFields .Any(t => t.Item1.Equals(definition) && t.Item2.FieldId == f.Id)); foreach (var qpField in fieldsToAdd) { contentSchema.Properties[qpField.Name] = GetPlainFieldSchema(qpField); } } if (forList) { return new JSchema { Type = JSchemaType.Array, Items = { contentSchema } }; } return contentSchema; }
/// <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); } } }