public void AddDataObjectTest() { DataObjectCache target = new DataObjectCache(); // TODO: Initialize to an appropriate value DataObject dobj = null; // TODO: Initialize to an appropriate value target.AddDataObject(dobj); }
/// <summary> /// Извлечение мастера из сериализованного представления /// </summary> /// <param name="masternode"> Текущий элемент xml </param> /// <param name="dataObject"> Текущий объект данных </param> /// <param name="assemblies"> Необходимые сборки </param> /// <param name="DataObjectCache"> DataObjectCache </param> /// <param name="deserializedObjectsList"> Словарь десериализованных объектов с их первичными ключами </param> private static void prv_ReadMaster( XmlNode masternode, ICSSoft.STORMNET.DataObject dataObject, SortedList assemblies, DataObjectCache DataObjectCache, Dictionary <string, ICSSoft.STORMNET.DataObject> deserializedObjectsList) { XmlNode specialTypeNode = masternode.Attributes.GetNamedItem("__Type"); string skey = masternode.Attributes.GetNamedItem("__PrimaryKey").Value; DataObject masterobject = null; if (deserializedObjectsList.ContainsKey(skey)) { masterobject = deserializedObjectsList[skey]; } else { string stype = specialTypeNode != null ? specialTypeNode.Value : masternode.Attributes.GetNamedItem("Type").Value; string asmName = (string)assemblies[stype]; Assembly asm = AssemblyLoader.LoadAssembly(asmName); Type mastertype = asm.GetType(stype); masterobject = DataObjectCache.CreateDataObject(mastertype, Information.TranslateValueToPrimaryKeyType(mastertype, skey)); if (specialTypeNode != null) { // То есть это был особым образом сериализованный мастер prv_XmlElement2DataObject( (XmlElement)masternode, masterobject, assemblies, DataObjectCache, deserializedObjectsList); } } Information.SetPropValueByName(dataObject, masternode.Name, masterobject); }
public void StartCachingTest() { DataObjectCache target = new DataObjectCache(); // TODO: Initialize to an appropriate value bool ClipParentCache = false; // TODO: Initialize to an appropriate value target.StartCaching(ClipParentCache); }
public void GetLivingDataObjectTest() { DataObjectCache target = new DataObjectCache(); // TODO: Initialize to an appropriate value Type typeofdataobject = null; // TODO: Initialize to an appropriate value object key = null; // TODO: Initialize to an appropriate value DataObject expected = null; // TODO: Initialize to an appropriate value DataObject actual; actual = target.GetLivingDataObject(typeofdataobject, key); Assert.Equal(expected, actual); }
/// <summary> /// Имитация загрузки объекта с транзакцией. /// </summary> /// <param name="dataObjectView">Представление для загрузки.</param> /// <param name="dobject">Объект для загрузки.</param> /// <param name="сlearDataObject">Следует ли очистить объект перед загрузкой.</param> /// <param name="сheckExistingObject">Проводить проверку существования ссылочных объектов.</param> /// <param name="dataObjectCache">Кэш загрузки объектов.</param> /// <param name="connection">Соединение для обновления.</param> /// <param name="transaction">Транзакция для обновления.</param> public override void LoadObjectByExtConn( View dataObjectView, DataObject dobject, bool сlearDataObject, bool сheckExistingObject, DataObjectCache dataObjectCache, IDbConnection connection, IDbTransaction transaction) { SetViewProperties(dobject, dataObjectView, LoadedValue); Counter++; }
/// <summary> /// Извлечение детейла из сериализованного представления /// </summary> /// <param name="xmldetailobjects"> Текущий элемент xml </param> /// <param name="detail"> Текущий список детейлов </param> /// <param name="assemblies"> Необходимые сборки </param> /// <param name="DataObjectCache"> DataObjectCache </param> /// <param name="deserializedObjectsList"> Словарь десериализованных объектов с их первичными ключами </param> private static void prv_ReadDetail( XmlNodeList xmldetailobjects, DetailArray detail, SortedList assemblies, DataObjectCache DataObjectCache, Dictionary <string, ICSSoft.STORMNET.DataObject> deserializedObjectsList) { for (int j = 0; j < xmldetailobjects.Count; j++) { XmlNode xmldetailobject = xmldetailobjects[j]; Assembly asm = AssemblyLoader.LoadAssembly((string)assemblies[xmldetailobject.Name]); System.Type dotype = asm.GetType(xmldetailobject.Name); DataObject detailobject = DataObjectCache.CreateDataObject(dotype, Information.TranslateValueToPrimaryKeyType(dotype, ((XmlElement)xmldetailobject).GetAttribute("__PrimaryKey"))); prv_XmlElement2DataObject((XmlElement)xmldetailobject, detailobject, assemblies, DataObjectCache, deserializedObjectsList); detail.AddObject(detailobject); } }
/// <summary> /// Получение объекта данных из ранее полученного XML документа /// </summary> /// <param name="dataObject"> Объект данных, в который будем десериализовывать </param> /// <param name="xmlDoc"> Сериализованный объект данных </param> public static void XMLDocument2DataObject(ref ICSSoft.STORMNET.DataObject dataObject, XmlDocument xmlDoc) { if (dataObject == null) { throw new ArgumentNullException("dataObject"); } var dataObjectCache = new DataObjectCache(); dataObjectCache.StartCaching(false); try { var xmlMainEl = (XmlElement)xmlDoc.FirstChild; XmlNode xmlNode = xmlMainEl.SelectSingleNode("Assemblies"); if (xmlNode != null) { XmlNodeList xmlAssemblies = xmlNode.ChildNodes; var assemblies = new SortedList(); for (int i = 0; i < xmlAssemblies.Count; i++) { assemblies.Add(xmlAssemblies[i].Name, ((XmlElement)xmlAssemblies[i]).GetAttribute("Assembly")); } var xmlEl = (XmlElement)xmlMainEl.FirstChild; if (xmlEl.Name == "Assemblies") { xmlEl = (XmlElement)xmlMainEl.LastChild; } prv_XmlElement2DataObject(xmlEl, dataObject, assemblies, dataObjectCache, new Dictionary <string, DataObject>()); } else { throw new Exception("Не найдено описание подключаемых сборок в сериализованном объекте"); } } finally { dataObjectCache.StopCaching(); } }
/// <summary> /// Прочитать свойство объекта (с целью его дальнейшей десериализации) /// </summary> /// <param name="xmlEl"> Текущий элемент xml </param> /// <param name="dataObject"> Текущий объект данных </param> /// <param name="propname"> Читаемое свойство объекта </param> /// <param name="assemblies"> Необходимые сборки </param> /// <param name="DataObjectCache"> DataObjectCache </param> /// <param name="deserializedObjectsList"> Словарь десериализованных объектов с их первичными ключами </param> private static void prv_ReadProperty( XmlElement xmlEl, ICSSoft.STORMNET.DataObject dataObject, string propname, SortedList assemblies, DataObjectCache DataObjectCache, Dictionary <string, ICSSoft.STORMNET.DataObject> deserializedObjectsList) { Type proptype = Information.GetPropertyType(dataObject.GetType(), propname); if (proptype.IsSubclassOf(typeof(DataObject))) { // Значит, мастер XmlNode masternode = xmlEl.GetElementsByTagName(propname)[0]; prv_ReadMaster(masternode, dataObject, assemblies, DataObjectCache, deserializedObjectsList); } else { if (proptype.IsSubclassOf(typeof(DetailArray))) { // Значит, детейл var detail = (DetailArray)Information.GetPropValueByName(dataObject, propname); XmlNode detailnode = xmlEl.GetElementsByTagName(propname)[0]; if (detailnode != null) { XmlNodeList xmldetailobjects = detailnode.ChildNodes; if (xmldetailobjects != null) { prv_ReadDetail(xmldetailobjects, detail, assemblies, DataObjectCache, deserializedObjectsList); } } } else { // Значит, это обычный атрибут XmlAttribute attr = xmlEl.GetAttributeNode(propname); if (attr != null) { prv_ReadAttribute(attr, dataObject); } } } }
/// <summary> /// Извлечение объекта данных из строки /// </summary> /// <param name="xmlEl"> Текущий элемент xml </param> /// <param name="dataObject"> Текущий объект данных </param> /// <param name="assemblies"> Необходимые сборки </param> /// <param name="DataObjectCache"> DataObjectCache </param> /// <param name="deserializedObjectsList"> Словарь десериализованных объектов с их первичными ключами </param> private static void prv_XmlElement2DataObject( XmlElement xmlEl, ICSSoft.STORMNET.DataObject dataObject, SortedList assemblies, DataObjectCache DataObjectCache, Dictionary <string, ICSSoft.STORMNET.DataObject> deserializedObjectsList) { if (!deserializedObjectsList.ContainsKey(dataObject.__PrimaryKey.ToString())) { deserializedObjectsList.Add(dataObject.__PrimaryKey.ToString(), dataObject); } var storableprops = new ArrayList(Information.GetStorablePropertyNames(dataObject.GetType())); var order = new StringCollection(); order.AddRange(Information.GetLoadingOrder(dataObject.GetType())); foreach (string propname in order) { // Прочитка в соответствии с указанным порядком prv_ReadProperty(xmlEl, dataObject, propname, assemblies, DataObjectCache, deserializedObjectsList); } XmlAttributeCollection xmlattributes = xmlEl.Attributes; XmlNodeList xmlchilds = xmlEl.ChildNodes; if (xmlattributes != null) { foreach (XmlAttribute xmlattribute in xmlattributes) { if (!order.Contains(xmlattribute.Name) && storableprops.Contains(xmlattribute.Name)) { prv_ReadAttribute(xmlattribute, dataObject); } } } if (xmlchilds != null) { foreach (XmlNode xmlchild in xmlchilds) { Type proptype = Information.GetPropertyType(dataObject.GetType(), xmlchild.Name); if (proptype.IsSubclassOf(typeof(DataObject))) { // Это мастер prv_ReadMaster(xmlchild, dataObject, assemblies, DataObjectCache, deserializedObjectsList); } else { // Это детейл if (!order.Contains(xmlchild.Name)) { var detail = (DetailArray)Information.GetPropValueByName(dataObject, xmlchild.Name); XmlNodeList xmldetailobjects = xmlchild.ChildNodes; if (xmldetailobjects != null) { prv_ReadDetail(xmldetailobjects, detail, assemblies, DataObjectCache, deserializedObjectsList); } } } } } if (xmlEl.HasAttribute("DynamicProperties")) { if (xmlEl.HasAttribute("DynamicProperties")) { string dpstr = xmlEl.GetAttribute("DynamicProperties"); if (string.IsNullOrEmpty(dpstr)) { dataObject.DynamicProperties = new NameObjectCollection(); } else { dataObject.DynamicProperties = (NameObjectCollection)ObjectFromString(dpstr); } } } dataObject.InitDataCopy(); dataObject.SetLoadingState(LoadingState.Loaded); dataObject.SetStatus(ObjectStatus.UnAltered); }
/// <summary> /// Заполнить объект данных. /// </summary> /// <param name="dobject">Объект данных.</param> /// <param name="values">Значения для заполнения.</param> /// <param name="storageStruct">Метаданные структуры хранения.</param> /// <param name="customizationStruct">Настройка выборки данных.</param> /// <param name="typesByKeys">Служебная структура, увязывающая типы мастеров и их ключи.</param> /// <param name="advCols">Дополнительные колонки.</param> /// <param name="dataObjectCache">Кэш объектов данных.</param> /// <param name="securityManager">Менеджер полномочий.</param> public static void FillRowSetToDataObject(DataObject dobject, object[] values, StorageStructForView storageStruct, LoadingCustomizationStruct customizationStruct, System.Collections.SortedList typesByKeys, AdvansedColumn[] advCols, DataObjectCache dataObjectCache, ISecurityManager securityManager) { Type dobjectType = dobject.GetType(); /* access type */ if (!securityManager.AccessObjectCheck(dobjectType, tTypeAccess.Full, false)) { securityManager.AccessObjectCheck(dobjectType, tTypeAccess.Read, true); } /* access type */ // Заливаем данные в объект данных. int customizationStructViewPropertiesLength = customizationStruct.View.Properties.Length; int advColsLength = advCols.Length; Information.SetPropValueByName(dobject, "__PrimaryKey", values[customizationStructViewPropertiesLength + advColsLength]); // 1. создаем структуру мастеров(свойств-объектов данных). System.Collections.SortedList assList = new System.Collections.SortedList(); int index = customizationStructViewPropertiesLength + 1 + advColsLength; CreateMastersStruct(dobject, values, ref index, 0, storageStruct.sources, assList, typesByKeys, dataObjectCache); assList.Add(storageStruct.sources, new object[] { dobject, 0 }); // 2. заливаем данные. System.Collections.ArrayList properiesValues = new System.Collections.ArrayList(); StringCollection allAdvCols = new StringCollection(); int masterPosition = index; for (int i = 0; i < advColsLength; i++) { object value = values[i + customizationStructViewPropertiesLength]; if (value == DBNull.Value) { value = null; } properiesValues.Add(new[] { advCols[i].Name, value, dobject }); allAdvCols.Add(advCols[i].Name); dobject.DynamicProperties.Add(advCols[i].Name, null); } for (int i = 0; i < customizationStructViewPropertiesLength; i++) { StorageStructForView.PropStorage prop = storageStruct.props[i]; if (Information.IsStoredProperty(dobjectType, prop.Name) || prop.Expression != null) { if (prop.MastersTypes == null) { object[] tmp = (object[])assList[prop.source]; object value; if (customizationStruct.ColumnsOrder != null && customizationStruct.ColumnsOrder.Length >= customizationStructViewPropertiesLength) { value = values[Array.IndexOf(customizationStruct.ColumnsOrder, prop.Name)]; } else { value = values[i]; } if (value == DBNull.Value) { value = null; } if (tmp != null) { properiesValues.Add( new[] { prop.simpleName, value, tmp[0] }); } } else { object[] tmp = (object[])assList[prop.source]; if (tmp != null) { // Ищем позицию. int tmp1 = (int)tmp[1]; int curMasterPosition = masterPosition; for (int j = 0; j < tmp1; j++) { curMasterPosition += prop.MastersTypes[j].Length; } int k = 0; object value = values[curMasterPosition]; if (value == DBNull.Value) { value = null; } while (k < prop.MastersTypes[tmp1].Length - 1 && value == null) { k++; value = values[curMasterPosition + k]; if (value == DBNull.Value) { value = null; } } object tmp0 = tmp[0]; if (value != null) { if (Information.GetPropValueByName((DataObject)tmp0, prop.simpleName) == null) { DataObject no = dataObjectCache.CreateDataObject(prop.MastersTypes[tmp1][k], value); if (no.GetStatus(false) == ObjectStatus.Created) { no.SetStatus(ObjectStatus.UnAltered); no.SetLoadingState(LoadingState.LightLoaded); no.InitDataCopy(dataObjectCache); } value = no; properiesValues.Add(new[] { prop.simpleName, value, tmp0 }); } else { // changed by fat properiesValues.Add(new[] { prop.simpleName, Information.GetPropValueByName((DataObject)tmp0, prop.simpleName), tmp0 }); } } else { properiesValues.Add(new[] { prop.simpleName, null, tmp0 }); } } masterPosition += prop.MastersTypesCount; } } } // 2.2 Записываем в объекты. System.Collections.SortedList curObjProperiesValues = new System.Collections.SortedList(); while (properiesValues.Count > 0) { // a. Выбираем для текущего объекта все свойства. object[] tmp = (object[])properiesValues[0]; DataObject curobj = (DataObject)tmp[2]; dobjectType = curobj.GetType(); curObjProperiesValues.Clear(); List <string> loadedPropsColl = curobj.GetLoadedPropertiesList(); for (int i = properiesValues.Count - 1; i >= 0; i--) { tmp = (object[])properiesValues[i]; if (tmp[2] == curobj) { object tmp0 = tmp[0]; if (!curObjProperiesValues.ContainsKey(tmp0)) { curObjProperiesValues.Add(tmp0, tmp[1]); if (!loadedPropsColl.Contains((string)tmp0)) { loadedPropsColl.Add((string)tmp0); } } properiesValues.RemoveAt(i); } } // b. Раскидываем согласно LoadOrder; string[] loadOrder = Information.GetLoadingOrder(dobjectType); int loadOrderLength = loadOrder.Length; for (int i = 0; i < loadOrderLength; i++) { string propName = loadOrder[i]; if (curObjProperiesValues.ContainsKey(propName)) { Information.SetPropValueByName(curobj, propName, curObjProperiesValues[propName]); curObjProperiesValues.Remove(propName); } } int curObjPropertiesValuesCount = curObjProperiesValues.Count; for (int i = 0; i < curObjPropertiesValuesCount; i++) { Information.SetPropValueByName(curobj, (string)curObjProperiesValues.GetKey(i), curObjProperiesValues.GetByIndex(i)); } if (loadedPropsColl.Count >= Information.GetAllPropertyNames(dobjectType).Length) { curobj.SetLoadingState(LoadingState.Loaded); } else { curobj.SetLoadingState(LoadingState.LightLoaded); curobj.AddLoadedProperties(loadedPropsColl); } curobj.SetStatus(ObjectStatus.UnAltered); } }
public void StopCachingTest() { DataObjectCache target = new DataObjectCache(); // TODO: Initialize to an appropriate value target.StopCaching(); }
public void DataObjectCacheConstructorTest() { DataObjectCache target = new DataObjectCache(); Assert.NotNull(target); }
/// <summary> /// Обновить хранилище по объектам. При ошибках делается попытка возобновления транзакции с другого запроса, /// т.к. предполагается, что запросы должны быть выполнены в другом порядке. /// </summary> /// <param name="objects">Объекты данных для обновления.</param> /// <param name="DataObjectCache">Кэш объектов данных.</param> public virtual void UpdateObjects(ref DataObject[] objects, DataObjectCache DataObjectCache) { UpdateObjects(ref objects, DataObjectCache, false); }
/// <summary> /// Обновить хранилище по объектам (есть параметр, указывающий, всегда ли необходимо взводить ошибку /// и откатывать транзакцию при неудачном запросе в базу данных). Если /// он true, всегда взводится ошибка. Иначе, выполнение продолжается. /// Однако, при этом есть опасность преждевременного окончания транзакции, с переходом для остальных /// запросов режима транзакционности в autocommit. Проявлением проблемы являются ошибки навроде: /// The COMMIT TRANSACTION request has no corresponding BEGIN TRANSACTION /// </summary> /// <param name="objects">Объекты для обновления.</param> /// <param name="DataObjectCache">Кэш объектов данных.</param> /// <param name="AlwaysThrowException">Если произошла ошибка в базе данных, не пытаться выполнять других запросов, сразу взводить ошибку и откатывать транзакцию.</param> public virtual void UpdateObjects(ref DataObject[] objects, DataObjectCache DataObjectCache, bool AlwaysThrowException) { object id = BusinessTaskMonitor.BeginTask("Update objects"); if (!DoNotChangeCustomizationString && ChangeCustomizationString != null) { var tps = new List <Type>(); foreach (DataObject d in objects) { Type t = d.GetType(); if (!tps.Contains(t)) { tps.Add(t); } } string cs = ChangeCustomizationString(tps.ToArray()); customizationString = string.IsNullOrEmpty(cs) ? customizationString : cs; } // Перенесли этот метод повыше, потому что строка соединения может быть сменена в бизнес-сервере делегатом смены строки соединения (если что-нибудь почитают). IDbConnection conection = GetConnection(); var DeleteQueries = new StringCollection(); var UpdateQueries = new StringCollection(); var UpdateFirstQueries = new StringCollection(); var InsertQueries = new StringCollection(); var DeleteTables = new StringCollection(); var UpdateTables = new StringCollection(); var InsertTables = new StringCollection(); var TableOperations = new SortedList(); var QueryOrder = new StringCollection(); var AllQueriedObjects = new ArrayList(); var auditOperationInfoList = new List <AuditAdditionalInfo>(); var extraProcessingList = new List <DataObject>(); GenerateQueriesForUpdateObjects(DeleteQueries, DeleteTables, UpdateQueries, UpdateFirstQueries, UpdateTables, InsertQueries, InsertTables, TableOperations, QueryOrder, true, AllQueriedObjects, DataObjectCache, extraProcessingList, objects); GenerateAuditForAggregators(AllQueriedObjects, DataObjectCache, ref extraProcessingList); OnBeforeUpdateObjects(AllQueriedObjects); Exception ex = null; /*access checks*/ foreach (DataObject dtob in AllQueriedObjects) { Type dobjType = dtob.GetType(); if (!SecurityManager.AccessObjectCheck(dobjType, tTypeAccess.Full, false)) { switch (dtob.GetStatus(false)) { case ObjectStatus.Created: SecurityManager.AccessObjectCheck(dobjType, tTypeAccess.Insert, true); break; case ObjectStatus.Altered: SecurityManager.AccessObjectCheck(dobjType, tTypeAccess.Update, true); break; case ObjectStatus.Deleted: SecurityManager.AccessObjectCheck(dobjType, tTypeAccess.Delete, true); break; } } } /*access checks*/ if (DeleteQueries.Count > 0 || UpdateQueries.Count > 0 || InsertQueries.Count > 0) { // Порядок выполнения запросов: delete,insert,update if (AuditService.IsAuditEnabled) { /* Аудит проводится именно здесь, поскольку на этот момент все бизнес-сервера на объектах уже выполнились, * объекты находятся именно в том состоянии, в каком должны были пойти в базу + в будущем можно транзакцию передать на исполнение */ AuditOperation(extraProcessingList, auditOperationInfoList); // TODO: подумать, как записывать аудит до OnBeforeUpdateObjects, но уже потенциально с транзакцией } conection.Open(); IDbTransaction trans = null; string query = string.Empty; string prevQueries = string.Empty; object subTask = null; try { trans = CreateTransaction(conection); IDbCommand command = conection.CreateCommand(); command.Transaction = trans; #region прошли вглубь обрабатывая only Update||Insert bool go = true; do { string table = QueryOrder[0]; if (!TableOperations.ContainsKey(table)) { TableOperations.Add(table, OperationType.None); } var ops = (OperationType)TableOperations[table]; if ((ops & OperationType.Delete) != OperationType.Delete) { // Смотрим есть ли Инсерты if ((ops & OperationType.Insert) == OperationType.Insert) { if ( (ex = RunCommands(InsertQueries, InsertTables, table, command, id, AlwaysThrowException)) == null) { ops = Minus(ops, OperationType.Insert); TableOperations[table] = ops; } else { go = false; } } // Смотрим есть ли Update if (go && ((ops & OperationType.Update) == OperationType.Update)) { if ((ex = RunCommands(UpdateQueries, UpdateTables, table, command, id, AlwaysThrowException)) == null) { ops = Minus(ops, OperationType.Update); TableOperations[table] = ops; } else { go = false; } } if (go) { QueryOrder.RemoveAt(0); go = QueryOrder.Count > 0; } } else { go = false; } }while (go); #endregion if (QueryOrder.Count > 0) { #region сзади чистые Update go = true; int queryOrderIndex = QueryOrder.Count - 1; do { string table = QueryOrder[queryOrderIndex]; if (TableOperations.ContainsKey(table)) { var ops = (OperationType)TableOperations[table]; if (ops == OperationType.Update) { if ( (ex = RunCommands(UpdateQueries, UpdateTables, table, command, id, AlwaysThrowException)) == null) { ops = Minus(ops, OperationType.Update); TableOperations[table] = ops; } else { go = false; } if (go) { queryOrderIndex--; go = queryOrderIndex >= 0; } } else { go = false; } } else { queryOrderIndex--; } }while (go); #endregion } foreach (string table in QueryOrder) { if ((ex = RunCommands(UpdateFirstQueries, UpdateTables, table, command, id, AlwaysThrowException)) != null) { throw ex; } } // Удаляем в обратном порядке. for (int i = QueryOrder.Count - 1; i >= 0; i--) { string table = QueryOrder[i]; if ((ex = RunCommands(DeleteQueries, DeleteTables, table, command, id, AlwaysThrowException)) != null) { throw ex; } } // А теперь опять с начала foreach (string table in QueryOrder) { if ((ex = RunCommands(InsertQueries, InsertTables, table, command, id, AlwaysThrowException)) != null) { throw ex; } if ((ex = RunCommands(UpdateQueries, UpdateTables, table, command, id, AlwaysThrowException)) != null) { throw ex; } } if (AuditService.IsAuditEnabled && auditOperationInfoList.Count > 0) { // Нужно зафиксировать операции аудита (то есть сообщить, что всё было корректно выполнено и запомнить время) AuditService.RatifyAuditOperationWithAutoFields( tExecutionVariant.Executed, AuditAdditionalInfo.SetNewFieldValuesForList(trans, this, auditOperationInfoList), this, true); } if (trans != null) { trans.Commit(); } } catch (Exception excpt) { if (trans != null) { trans.Rollback(); } if (AuditService.IsAuditEnabled && auditOperationInfoList.Count > 0) { // Нужно зафиксировать операции аудита (то есть сообщить, что всё было откачено) AuditService.RatifyAuditOperationWithAutoFields(tExecutionVariant.Failed, auditOperationInfoList, this, false); } conection.Close(); BusinessTaskMonitor.EndSubTask(subTask); throw new ExecutingQueryException(query, prevQueries, excpt); } conection.Close(); var res = new ArrayList(); foreach (DataObject changedObject in objects) { changedObject.ClearPrototyping(true); if (changedObject.GetStatus(false) != STORMDO.ObjectStatus.Deleted) { Utils.UpdateInternalDataInObjects(changedObject, true, DataObjectCache); res.Add(changedObject); } } foreach (DataObject dobj in AllQueriedObjects) { if (dobj.GetStatus(false) != STORMDO.ObjectStatus.Deleted && dobj.GetStatus(false) != STORMDO.ObjectStatus.UnAltered) { Utils.UpdateInternalDataInObjects(dobj, true, DataObjectCache); } } objects = new DataObject[res.Count]; res.CopyTo(objects); BusinessTaskMonitor.EndTask(id); } if (AfterUpdateObjects != null) { AfterUpdateObjects(this, new DataObjectsEventArgs(objects)); } }