/// <summary> /// Запускает сохранение в рамках текущей трпнзакции. /// </summary> /// <param name="xs">Реализация XStorageConnection</param> /// <param name="datagram">множество объектов</param> protected void saveWithinTransaction(XStorageConnection xs, XDatagram datagram) { const string SAVEPOINT_NAME = "SP_XStorage_Save"; bool bSavePointUsed = xs.IsSavePointAllowed; if (bSavePointUsed) { xs.SetSavePoint(SAVEPOINT_NAME); } try { DoSave(xs, datagram); if (bSavePointUsed) { xs.ReleaseSavePoint(); } } catch (XDbDeadlockException) { // если произошел Dealock, то не будем пытаться откатить транзакцию, т.к. она уже не существует throw; } catch { if (bSavePointUsed) { xs.RollbackToSavePoint(); } throw; } }
/// <summary> /// Сохранение данных множества объектов. /// </summary> /// <remarks> /// Управление транзакцией внешнее. /// </remarks> /// <param name="context"></param> /// <param name="dataSet"></param> /// <param name="transactionID"></param> public static void Save(IXExecutionContext context, DomainObjectDataSet dataSet, Guid transactionID) { // #1: Вызов триггеров Before XTriggersController.Instance.FireTriggers(dataSet, XTriggerFireTimes.Before, context); // #2: Сбросим закэшированные данные объектов IEnumerator enumerator = dataSet.GetModifiedObjectsEnumerator(false); while (enumerator.MoveNext()) { DomainObjectData xobj = (DomainObjectData)enumerator.Current; // Примечание: для новых объектов сбрасывать кэш бессмысленно - их там нет if (!xobj.IsNew) { DomainObjectRegistry.ResetObject(xobj); } } // #3: Запись данных XDatagramProcessorEx dg_proc = XDatagramProcessorMsSqlEx.Instance; XDatagramBuilder dgBuilder = dg_proc.GetDatagramBuilder(); XDatagram dg = dgBuilder.GetDatagram(dataSet); dg_proc.Save(context.Connection, dg); // #4: Сохранение chunked-данных saveChunkedData(transactionID, dg, context.Connection); // #5: Сигнализируем Securitymanager, что обновились данные (для очистки кэшей) XSecurityManager.Instance.TrackModifiedObjects(dataSet); // #6: Вызов триггеров After XTriggersController.Instance.FireTriggers(dataSet, XTriggerFireTimes.After, context); }
/// <summary> /// Формирует список устаревших и список удаленных объектов после неудачной процедуры обновления. /// Метод должен вызываться в рамках процедуры обновления измененных объектов когда количество обновленных /// объектов в БД не совпало с ожидаемым количество. Это означает, что некоторые объекты (из списка aUptObjects) /// либо "устарели", либо удалены. Данный метод как раз выявляет эти объекты /// Списки objects_obsolete и objects_deleted содержат объекты типов XObjectIdentity. /// </summary> /// <param name="xs">Наследник XStorageConnection</param> /// <param name="aUptObjects">Список обновляемых объектов</param> /// <param name="objects_obsolete">Возвращаемый список устаревших объектов</param> /// <param name="objects_deleted">Возвращаемый список удаленных объектов</param> protected void getOutdatedObjects(XStorageConnection xs, IList aUptObjects, out ArrayList objects_obsolete, out ArrayList objects_deleted) { XDbCommand cmd; // команда ArrayList objects_notdeleted = new ArrayList(); // список не удаленных объектов objects_obsolete = new ArrayList(); // список устаревших объектов objects_deleted = new ArrayList(); // список удаленных объектов XStorageObjectToSave xobjFirst; // первый объект группы однотипных объектов // пойдем по группам однотипных объектов IEnumerator enumerator = XDatagram.GetEnumerator(aUptObjects); StringBuilder cmdBuilder = new StringBuilder(); while (enumerator.MoveNext()) { ArrayList aGroup = (ArrayList)enumerator.Current; xobjFirst = (XStorageObjectToSave)aGroup[0]; cmdBuilder.Length = 0; cmdBuilder.AppendFormat("SELECT {0}, ts FROM {1} WHERE ", xs.ArrangeSqlName("ObjectID"), // 0 xs.GetTableQName(xobjFirst.SchemaName, xobjFirst.ObjectType) // 1 ); foreach (XStorageObjectToSave xobj in aGroup) { cmdBuilder.AppendFormat("{0}={1} OR ", xs.ArrangeSqlName("ObjectID"), xs.ArrangeSqlGuid(xobj.ObjectID)); } // отрежем последний " OR " cmdBuilder.Length -= 4; // получим список удаленных объектов: // для этого сначала получим список неудаленных объектов objects_notdeleted.Clear(); cmd = xs.CreateCommand(cmdBuilder.ToString()); using (IDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { // под индексом 0 здесь ObjectID объекта, он всегда NOT NULL, поэтому IsDbNull не проверяем objects_notdeleted.Add(new XObjectIdentity(xobjFirst.ObjectType, reader.GetGuid(0), reader.GetInt64(1))); } } // теперь у нас есть изначальный список объектов, и список неудаленных объектов, их разность и будет список удаленных объектов foreach (XStorageObjectToSave xobj in aGroup) { // объект для поиска XObjectIdentity xobjID = new XObjectIdentity(xobj.ObjectType, xobj.ObjectID); int nIndex = objects_notdeleted.IndexOf(xobjID); // если текущего объекта нет в списке неудаленных объектов, значит он удаленный if (nIndex == -1) { objects_deleted.Add(xobjID); } // иначе, если у зачитанного объекта отличается ts от текущего, значит он устаревший else if ((objects_notdeleted[nIndex] as XObjectIdentity).TS != xobj.TS && xobj.AnalyzeTS) { objects_obsolete.Add(xobjID); } } } }
/// <summary> /// Сохраняет бинарные и большие текстовые свойства у всех объектов /// (из списка новых и списка обновляемых объектов) /// </summary> /// <param name="xs">Реализация XStorageConnection</param> /// <param name="datagram">Множество обрабатываемых объектов</param> protected void updateBinAndLongData(XStorageConnection xs, XDatagram datagram) { foreach (XStorageObjectToSave xobj in datagram.ObjectsToInsert) { updateBinAndLongDataForObject(xs, xobj); } foreach (XStorageObjectToSave xobj in datagram.ObjectsToUpdate) { updateBinAndLongDataForObject(xs, xobj); } }
/// <summary> /// Обновляет кросс-таблицы для массивных свойств (collection, collection-membership, array) /// </summary> /// <param name="xs">Реализация XStorageConnection</param> /// <param name="datagram">датаграмма</param> protected void updateCrossTables(XStorageConnection xs, XDatagram datagram) { XDbStatementDispatcher disp = xs.CreateStatementDispatcher(); foreach (XStorageObjectToSave xobj in datagram.ObjectsToInsert) { updateCrossTablesForObject(xs, disp, xobj); } foreach (XStorageObjectToSave xobj in datagram.ObjectsToUpdate) { updateCrossTablesForObject(xs, disp, xobj); } disp.ExecutePendingStatementsAndReturnTotalRowsAffected(); }
public XDatagram GetDatagram(DomainObjectDataSet dataSet) { XDatagram dg = new XDatagram(); IEnumerator enumerator = dataSet.GetModifiedObjectsEnumerator(false); while (enumerator.MoveNext()) { XStorageObjectBase xobj = createXStorageObject((DomainObjectData)enumerator.Current); add(xobj); createObjectsFromLinks(xobj); } fillDatagram(dg); return(dg); }
/// <summary> /// Расносит объекты из списка m_objects по 3-м списка в переданной датаграмме: /// списку удаляемых, обновляемых, вставляемых /// </summary> protected void fillDatagram(XDatagram dg) { foreach (XStorageObjectBase xobj in m_objects.Values) { if (xobj.State == XStorageObjectState.ToDelete) { dg.AddDeleted((XStorageObjectToDelete)xobj); } else if (xobj.State == XStorageObjectState.ToInsert) { dg.AddInserted((XStorageObjectToSave)xobj); } else { dg.AddUpdated((XStorageObjectToSave)xobj); } } }
/// <summary> /// Запускает сохранение при отсутствии внещней транзакции, создает транзакцию внутри /// </summary> /// <param name="xs">Реализация XStorageConnection</param> /// <param name="datagram">множество объектов</param> protected void saveWithoutTransaction(XStorageConnection xs, XDatagram datagram) { xs.BeginTransaction(); try { DoSave(xs, datagram); xs.CommitTransaction(); } catch (XDbDeadlockException) { // если произошел Dealock, то не будем пытаться откатить транзакцию, т.к. она уже не существует throw; } catch { xs.RollbackTransaction(); throw; } }
/// <summary> /// Сохраняет chunked-данные всех объектов из датаграммы /// </summary> /// <param name="transactionID">Идентификатор транзакции</param> /// <param name="datagram">датаграмма</param> /// <param name="con">соединение</param> protected static void saveChunkedData(Guid transactionID, XDatagram datagram, XStorageConnection con) { bool bChunkedDataFound = false; foreach (XStorageObjectToSave xobj in datagram.ObjectsToInsert) { bChunkedDataFound = saveObjectChunkedData(xobj, con); } foreach (XStorageObjectToSave xobj in datagram.ObjectsToUpdate) { bChunkedDataFound = bChunkedDataFound || saveObjectChunkedData(xobj, con); } // Если в процессе какие-либо "кусочные" данные были перегружены // в положенные таблицы - удаляем такие "куски": if (bChunkedDataFound) { XChunkStorageGateway.RemoveTransactionData(transactionID, con); } }
public void Save(XStorageConnection xs, XDatagram datagram) { if (xs == null) { throw new ArgumentNullException("xs"); } if (datagram == null) { throw new ArgumentNullException("xobjSet"); } Debug.Assert(datagram.ObjectsToInsert != null, "Массив объектов на вставку не определен (null)"); Debug.Assert(datagram.ObjectsToUpdate != null, "Массив объектов на обновление не определен (null)"); Debug.Assert(datagram.ObjectsToDelete != null, "Массив объектов на удаление не определен (null)"); if (xs.Transaction == null) { saveWithoutTransaction(xs, datagram); } else { saveWithinTransaction(xs, datagram); } }
public XDatagramWalker(XDatagram dg) { m_datagram = dg; }
/// <summary> /// Подчищает ссылки объектов, удаленных из линков /// </summary> /// <param name="xs">Реализация XStorageConnection</param> /// <param name="disp">диспетчер запросов</param> /// <param name="datagram">Множество обрабатываемых объектов</param> protected void purgeLinks(XStorageConnection xs, XDbStatementDispatcher disp, XDatagram datagram) { StringBuilder queryBuilder; // построитель запроса string sTypeName; // наименование типа объекта владельца обратного свойства if (datagram.ObjectsToUpdate.Count == 0) { return; // обновлять нечего } queryBuilder = new StringBuilder(); // по всем объектам из списка обновляемых foreach (XStorageObjectToSave xobj in datagram.ObjectsToUpdate) { foreach (DictionaryEntry entry in xobj.GetPropsByCapacity(XPropCapacity.Link, XPropCapacity.LinkScalar)) { XPropInfoObject propInfo = (XPropInfoObject)xobj.TypeInfo.GetProp((string)entry.Key); Guid[] values = (Guid[])entry.Value; queryBuilder.Length = 0; sTypeName = propInfo.ReferedType.Name; // всем объектам ссылающимся на текущуй объект по обратному относительно текущего свойству // обNULLим ссылку.. queryBuilder.AppendFormat( "UPDATE {0} SET {1} = NULL, {2} = CASE WHEN {2}<{3} THEN {2}+1 ELSE 1 END {5}WHERE {1}={4} ", xs.GetTableQName(xobj.TypeInfo.Schema, sTypeName), // 0 xs.ArrangeSqlName(propInfo.ReverseProp.Name), // 1 xs.ArrangeSqlName("ts"), // 2 Int64.MaxValue, // 3 xs.ArrangeSqlGuid(xobj.ObjectID), // 4 xs.Behavior.SqlNewLine // 5 ); // ...при условии, что идентификаторы этих объектов не упомянуты в свойстве if (values.Length > 0) { // в текущем св-ве есть объекты (их идентификаторы в values) queryBuilder.AppendFormat("AND NOT {0} IN (", xs.ArrangeSqlName("ObjectID")); foreach (Guid value in values) { queryBuilder.AppendFormat("{0},", xs.ArrangeSqlGuid(value)); } // отрежем последнюю запятую queryBuilder.Length--; queryBuilder.Append(") "); } // так же исключим объекты, присутствующие в списке удаляемых, // т.к. при удалении проверяется ts, а формируемый update его увеличивает, да и вообще бессмыселен if (datagram.ObjectsToDelete.Count > 0) { StringBuilder addWhereBuilder = new StringBuilder(); foreach (XStorageObjectToDelete xobjDel in datagram.ObjectsToDelete) { if (xobjDel.ObjectType == sTypeName) { addWhereBuilder.AppendFormat("{0},", xs.ArrangeSqlGuid(xobjDel.ObjectID)); } } if (addWhereBuilder.Length > 0) { // отрежем последнюю запятую addWhereBuilder.Length--; queryBuilder.AppendFormat("AND NOT {0} IN ({1}) ", xs.ArrangeSqlName("ObjectID"), // 0 addWhereBuilder.ToString() // 1 ); } } disp.DispatchStatement(queryBuilder.ToString(), false); } // конец цикла по свойствам объекта xobj } // конец цикла по объектам из списка обновляемых }
/// <summary> /// Вставляет новые объекты /// </summary> /// <param name="xs">Реализация XStorageConnection</param> /// <param name="disp">диспетчер запросов</param> /// <param name="datagram">Множество обрабатываемых объектов</param> /// <param name="bSuppressMagicBit">признак того, что надо исключить обработку поля MagicBit</param> protected virtual void insertObjects(XStorageConnection xs, XDbStatementDispatcher disp, XDatagram datagram, bool bSuppressMagicBit) { int nIndex; // порядковый индекс объекта XDbCommand cmd; // команда как фабрика для создания параметров // получим упорядоченный список новых объектов упорядоченный по индексу зависимости IList aInsObjects = datagram.ObjectsToInsert; if (aInsObjects.Count == 0) { return; } nIndex = -1; cmd = xs.CreateCommand(); // для каждого объекта создадим заготовку ADO-команды с оператором insert и коллекцией параметров foreach (XStorageObjectToSave xobj in aInsObjects) { insertObject(xs, disp, xobj, cmd, ++nIndex, bSuppressMagicBit); } }
/// <summary> /// Сохранение (вставка, обновление, удаление) множества объектов /// Не оборачивается ни транзакцией, ни savepoint'ами. /// </summary> /// <param name="xs">Реализация XStorageConnection</param> /// <param name="datagram">множество объектов</param> public abstract void DoSave(XStorageConnection xs, XDatagram datagram);