/// <summary> /// Сохраняет бинарные и большие текстовые свойства для переданного объекта /// </summary> /// <param name="xs">Реализация XStorageConnection</param> /// <param name="xobj">Объект, для которого требуется обновить большие (бинарные и текстовые) свойства</param> protected virtual void updateBinAndLongDataForObject(XStorageConnection xs, XStorageObjectToSave xobj) { string sValue; // по всем текстовым полям переданного объекта foreach (DictionaryEntry entry in xobj.GetPropsByType(XPropType.vt_text)) { // установка в NULL происходит сразу в insert/update'ах if (entry.Value == DBNull.Value) { sValue = null; } else { sValue = (string)entry.Value; } xs.SaveTextData(xobj.SchemaName, xobj.ObjectType, xobj.ObjectID, (string)entry.Key, sValue); } byte[] aValue; // по всем бинарным полям переданного объекта foreach (DictionaryEntry entry in xobj.GetPropsByType(XPropType.vt_bin)) { // установка в NULL происходит сразу в insert/update'ах if (entry.Value == DBNull.Value) { aValue = null; } else { aValue = (byte[])entry.Value; } xs.SaveBinData(xobj.SchemaName, xobj.ObjectType, xobj.ObjectID, (string)entry.Key, aValue); } }
public override void Merge(XStorageObjectBase p_xobj) { base.Merge(p_xobj); XStorageObjectToSave xobj = p_xobj as XStorageObjectToSave; if (xobj == null) { Debug.Fail("Некорректная реализация XStorageObjectBase::Merge"); throw new ArgumentException(); } UpdateTS = UpdateTS | xobj.UpdateTS; if (xobj.IsToInsert) { m_state = XStorageObjectState.ToInsert; } foreach (string sPropName in xobj.Props.Keys) { if (!Props.Contains(sPropName)) { // свойства не было - добавим Props[sPropName] = xobj.Props[sPropName]; } else { XStorageObjectPropMergeModes mergeModeThis = GetPropMergeMode(sPropName); XStorageObjectPropMergeModes mergeModeForeign = xobj.GetPropMergeMode(sPropName); // если хотя бы для одного свойства стоит флаг "слабой" проверки, то отключаем проверку на совпадание значений свойств if (mergeModeThis == XStorageObjectPropMergeModes.Replace) { Debug.Assert(mergeModeForeign != XStorageObjectPropMergeModes.Replace, "Два свойства с признаком перезаписи - это некорректная ситуация"); // у текущего св-ва задан атрибут перезаписи, поэтому оставляем его в неприкосновенности (даже проверки не делаем) } else if (mergeModeForeign == XStorageObjectPropMergeModes.Replace) { Props[sPropName] = xobj.Props[sPropName]; } else { if (!isPropsEquals(Props[sPropName], xobj.Props[sPropName])) { throw new XMergeConflictException("Значения свойста " + sPropName + " отличаются: '" + Props[sPropName] + "' и '" + xobj.Props[sPropName] + "'"); } } } } // смерджим словарь свойств, данные которых загружены механизмом кусочного сохранения if (xobj.PropertiesWithChunkedData.Count > 0) { foreach (string sPropName in xobj.PropertiesWithChunkedData.Keys) { PropertiesWithChunkedData[sPropName] = xobj.PropertiesWithChunkedData[sPropName]; } } ParticipateInUniqueIndex = xobj.ParticipateInUniqueIndex | ParticipateInUniqueIndex; }
public XStorageObjectBase createXStorageObject(DomainObjectData obj) { XStorageObjectBase xobj; object vPropValue; if (obj.ToDelete) { xobj = new XStorageObjectToDelete(obj.TypeInfo, obj.ObjectID, obj.TS, true); } else { xobj = new XStorageObjectToSave(obj.TypeInfo, obj.ObjectID, obj.TS, obj.IsNew); ((XStorageObjectToSave)xobj).PropertiesWithChunkedData = obj.PropertiesWithChunkedData; } bool bNeedTrackUniqueIndexParticipation = xobj.TypeInfo.HasUniqueIndexes && xobj.TypeInfo.DeferrableIndexes && !obj.ToDelete; foreach (string sPropName in obj.UpdatedPropNames) { vPropValue = obj.GetUpdatedPropValue(sPropName); XPropInfoBase propInfo = obj.TypeInfo.GetProp(sPropName); if (propInfo is XPropInfoNumeric) { if (((XPropInfoSimpleEnumerable)propInfo).IsEnum) { // в качестве значения свойства может быть поcле перечисления, надо привести его в элементарному типу if (vPropValue.GetType().IsEnum) { if (propInfo.VarType == XPropType.vt_i4) { vPropValue = (Int32)vPropValue; } else if (propInfo.VarType == XPropType.vt_i2) { vPropValue = (Int16)vPropValue; } else // if (propInfo.VarType == XPropType.vt_ui1) { vPropValue = (byte)vPropValue; } } } } if (vPropValue != null) { xobj.Props[sPropName] = vPropValue; } // если свойство участвует в уникальном индексе, запомним это if (bNeedTrackUniqueIndexParticipation) { if (xobj.TypeInfo.IsPropIncludedIntoUniqueIndex(sPropName)) { ((XStorageObjectToSave)xobj).ParticipateInUniqueIndex = true; } } } return(xobj); }
/// <summary> /// Добавление объекта в коллекцию обновляемых объектов. Позволяет добавлять один и тот же объект несколько раз. /// В отличии от новых и удаляемых объектов, обновляемые могут добавляться явно, т.к. это требуется для "отложенного обновления". /// Это случай когда объект сохраняется для SQL оператора: сначала INSERT, а потом UPDATE. /// Отложенное обновление требуется для сохранения сетей объектов (Например, А ссылается на Б, а Б ссылается на А) /// </summary> /// <param name="xobj">Объект для помещения в список обновляемых</param> public void AddUpdated(XStorageObjectToSave xobj) { XStorageObjectToSave xobjExists = (XStorageObjectToSave)m_hashObjectsToUpdate[getKey(xobj)]; if (xobjExists == null) { m_aObjectsToUpdate.Add(xobj); m_hashObjectsToUpdate.Add(getKey(xobj), xobj); } else { xobjExists.Merge(xobj); } }
/// <summary> /// Сохраняет chunked-данные заданного объекта /// </summary> /// <param name="xobj">объект</param> /// <param name="con">соединение с БД</param> /// <returns>true - объект содержал chunked данные, иначе false</returns> protected static bool saveObjectChunkedData(XStorageObjectToSave xobj, XStorageConnection con) { string sPropName; // наименование свойтсва Guid ownerID; // идентификатор цепочки кусочных данных свойства bool bChunkedDataFound = false; // найдем свойства, чьи данные были загруженны по частам foreach (DictionaryEntry entry in xobj.PropertiesWithChunkedData) { sPropName = (string)entry.Key; ownerID = (Guid)entry.Value; bChunkedDataFound = true; XChunkStorageGateway.MergePropertyChunkedData( ownerID, xobj.ObjectType, sPropName, xobj.ObjectID, con); } return(bChunkedDataFound); }
public XDatagramFromXml(XmlElement xmlRoot, XMetadataManager mdManager) { m_xmodel = mdManager.XModel; parseXmlForest(xmlRoot); checkReverseProps(); foreach (XStorageObjectBase xobj in m_objects) { // с момента вызова normalizeObject коллекция m_objects и словарь m_objectsDictionary не согласованы: // normalizeObject добавляет все создаваемые объекты в m_objectsDictionary, но не добавляет в коллекцию m_objects ArrayList aNewObject = normalizeObject(xobj); // если что-то вернут, то это будет список "новых" обновляемых объектов, // т.е. объекты которые надо проапдейтить в результате помещения ссылок на них в линки текущего объекта if (aNewObject != null) { foreach (XStorageObjectToSave xobjDetached in aNewObject) { addUpdatedInternal(xobjDetached); } } if (xobj is XStorageObjectToDelete) { addDeletedInternal((XStorageObjectToDelete)xobj); } else { XStorageObjectToSave xobjSave = (XStorageObjectToSave)xobj; if (xobjSave.IsToInsert) { addInsertedInternal(xobjSave); } else { addUpdatedInternal(xobjSave); } } } }
private void createObjectsFromLinks(XStorageObjectBase xobj) { // TODO: Здесь вопрос: удаленные объекты могут содержать линки с ссылками и, если да, то надо ли их обрабатывать ? if (xobj is XStorageObjectToDelete) { return; } XStorageObjectToSave xobjSave = (XStorageObjectToSave)xobj; foreach (DictionaryEntry entry in xobjSave.GetPropsByCapacity(XPropCapacity.Link, XPropCapacity.LinkScalar)) { Guid[] valueOIDs = (Guid[])entry.Value; if (valueOIDs.Length == 0) { continue; } string sPropName = (string)entry.Key; XPropInfoObjectLink propInfo = (XPropInfoObjectLink)xobj.TypeInfo.GetProp(sPropName); int nIndex = 0; foreach (Guid valueOID in valueOIDs) { XStorageObjectBase xobjValue = new XStorageObjectToSave(propInfo.ReferedType, valueOID, -1, false); xobjValue.Props[propInfo.ReverseProp.Name] = xobj.ObjectID; // пометим свойство специальным атрибутом, чтобы для него не выполнялась проверка на совпадение содержимого при мердже xobjValue.SetPropMergeMode(propInfo.ReverseProp.Name, XStorageObjectPropMergeModes.Weak); xobjValue = add(xobjValue); // упорядоченный линк ? - установим индексное свойство if (propInfo.OrderByProp != null) { xobjValue.Props[propInfo.OrderByProp.Name] = nIndex++; // пометим свойство специальным атрибутом, говорящим о том что при Merge'е данное значение затрет другое значение xobjValue.SetPropMergeMode(propInfo.OrderByProp.Name, XStorageObjectPropMergeModes.Replace); } } } }
private ArrayList normalizeObject(XStorageObjectBase xobj) { if (xobj is XStorageObjectToDelete) { return(null); } XStorageObjectToSave xobjSave = (XStorageObjectToSave)xobj; ArrayList aNewObjects = null; foreach (DictionaryEntry entry in xobjSave.GetPropsByCapacity(XPropCapacity.Link, XPropCapacity.LinkScalar)) { Guid[] valueOIDs = (Guid[])entry.Value; if (valueOIDs.Length == 0) { continue; } string sPropName = (string)entry.Key; XPropInfoObjectLink propInfo = (XPropInfoObjectLink)xobj.TypeInfo.GetProp(sPropName); int nIndex = 0; foreach (Guid valueOID in valueOIDs) { XStorageObjectBase xobjValue = (XStorageObjectBase)m_objectsDictionary[valueOID]; if (xobjValue == null) { // в датаграмме нет объекта, на который установлена ссылка в линке - значит создадим, этот объект точно не новый xobjValue = new XStorageObjectToSave(propInfo.ReferedType, valueOID, -1, false); if (aNewObjects == null) { aNewObjects = new ArrayList(); } aNewObjects.Add(xobjValue); // на случай если данный объект содержится еще в каком-нибудь линке m_objectsDictionary.Add(valueOID, xobjValue); } else { object vValue = xobjValue.Props[propInfo.ReverseProp.Name]; if (vValue == null || vValue == DBNull.Value) { // все хорошо - обратное свойство (объектный скаляр) пустое - установим его на текущий объект (xobj) xobjValue.Props[propInfo.ReverseProp.Name] = xobj.ObjectID; } else { Debug.Assert(vValue is Guid); // больше ничего другого быть не должно! // обратное свойство уже заполнено, проверим что оно ссылается на текущий объект. если это не так - ругаемся if (((Guid)vValue) != xobj.ObjectID) { throw new XInvalidXmlForestException("Ошибка при установке свойства " + propInfo.ReverseProp.Name + " объекта " + xobjValue.TypeInfo.Name + " [" + xobjValue.ObjectID + "]: нарушение согласованности со свойством " + sPropName + " объекта " + xobj.TypeInfo.Name + " [" + xobj.ObjectID + "]"); } } } // упорядоченный линк ? - установим индексное свойство if (propInfo.OrderByProp != null) { xobjValue.Props[propInfo.OrderByProp.Name] = nIndex++; } } } return(aNewObjects); }
public static XStorageObjectBase CreateXStorageObject(XmlElement xmlObject, XTypeInfo typeInfo) { XStorageObjectBase xobj; Guid oid = new Guid(xmlObject.GetAttribute("oid")); Int64 nTS; if (xmlObject.HasAttribute("ts")) { nTS = Int32.Parse(xmlObject.GetAttribute("ts")); } else { nTS = -1; } bool bIsNew = false; if (xmlObject.HasAttribute("delete")) { xobj = new XStorageObjectToDelete(typeInfo, oid, nTS, true); } else { bIsNew = xmlObject.HasAttribute("new"); xobj = new XStorageObjectToSave(typeInfo, oid, nTS, bIsNew); } bool bNeedTrackUniqueIndexParticipation = typeInfo.HasUniqueIndexes && typeInfo.DeferrableIndexes && xobj is XStorageObjectToSave; // по всем свойствам без признака loaded="0" foreach (XmlElement xmlProp in xmlObject.SelectNodes("*[not(@loaded)]")) { XPropInfoBase xprop = typeInfo.GetProp(xmlProp.LocalName); if (xprop == null) { continue; } // если на xml-свойстве неудаляемого объекта есть атрибут с идентификатором цепочки кусочных данных, то занесем его в специальный словарь if (xobj is XStorageObjectToSave && xmlProp.HasAttribute(ATTR_CHUNCK_CHAIN_ID)) { ((XStorageObjectToSave)xobj).PropertiesWithChunkedData.Add(xprop.Name, new Guid(xmlProp.GetAttribute(ATTR_CHUNCK_CHAIN_ID))); } // членство в массиве проигнорируем if (xprop is XPropInfoObjectArray) { if (((XPropInfoObjectArray)xprop).Capacity == XPropCapacity.ArrayMembership) { continue; } } // пустые массивные свойства новых объектов проигнорируем if (bIsNew) { if (xprop is XPropInfoObjectArray || xprop is XPropInfoObjectLink) { if (!xmlProp.HasChildNodes) { continue; } } } xobj.Props.Add(xprop.Name, getPropValue(xmlProp, xprop)); // если свойство участвует в уникальном индексе, запомним это if (bNeedTrackUniqueIndexParticipation) { if (typeInfo.IsPropIncludedIntoUniqueIndex(xprop.Name)) { ((XStorageObjectToSave)xobj).ParticipateInUniqueIndex = true; } } } return(xobj); }
/// <summary> /// Обновляет кросс-таблицы для массивных свойств (collection, collection-membership, array) заданного объекта /// </summary> /// <param name="xs">Реализация XStorageConnection</param> /// <param name="disp">диспетчер запросов</param> /// <param name="xobj">ds-Объект</param> protected void updateCrossTablesForObject(XStorageConnection xs, XDbStatementDispatcher disp, XStorageObjectToSave xobj) { string sPropName; // наименование свойства string sDBCrossTableName; // полное наименование кросс-таблицы int nIndex; // значение колонки k string sKeyColumn; // наименование колонки кросс таблицы, по которой будем очищать string sValueColumn; // // по всем свойствам текущего объекта вида: массив, коллекция, членство в коллекции: foreach (DictionaryEntry entry in xobj.GetPropsByCapacity(XPropCapacity.Collection, XPropCapacity.Array, XPropCapacity.CollectionMembership)) { sPropName = (string)entry.Key; XPropInfoObject propInfo = (XPropInfoObject)xobj.TypeInfo.GetProp(sPropName); Debug.Assert(entry.Value is Guid[]); Guid[] values = (Guid[])entry.Value; // сформируем наименование кросс-таблицы по поля, по которому будем очищать кросс-таблицу: sDBCrossTableName = xs.GetTableQName(xobj.SchemaName, xobj.TypeInfo.GetPropCrossTableName(sPropName)); // Сохранение массива: очистим по ObjectID и вставим все значения из свойства if (propInfo.Capacity == XPropCapacity.Array) { StringBuilder cmdBuilder = new StringBuilder(); // если сохраняем массив (array) нового объекта (отложенное обновление), то DELETE выполнять не будем if (!xobj.IsToInsert) { // сформируем необходимый оператор delete на кросс-таблицу: cmdBuilder.AppendFormat( "DELETE FROM {0} WHERE {1}={2}", sDBCrossTableName, // 0 xs.ArrangeSqlName("ObjectID"), // 1 xs.GetParameterName("ObjectID") // 2 ); disp.DispatchStatement( cmdBuilder.ToString(), new XDbParameter[] { xs.CreateCommand().CreateParameter("ObjectID", DbType.Guid, ParameterDirection.Input, false, xobj.ObjectID) }, false ); } nIndex = 0; // для каждого значения массивного свойства добавим INSERT в кросс-таблицу foreach (Guid value in values) { cmdBuilder.Length = 0; cmdBuilder.AppendFormat("INSERT INTO {0} ({1}, {2}, {3}) values ({4}, {5}, {6})", sDBCrossTableName, // 0 xs.ArrangeSqlName("ObjectID"), // 1 xs.ArrangeSqlName("Value"), // 2 xs.ArrangeSqlName("k"), // 3 xs.ArrangeSqlGuid(xobj.ObjectID), // 4 xs.ArrangeSqlGuid(value), // 5 nIndex // 6 ); disp.DispatchStatement(cmdBuilder.ToString(), false); ++nIndex; } } // Сохранение коллекции и членства в коллекции else { if (propInfo.Capacity == XPropCapacity.CollectionMembership) { sKeyColumn = "Value"; sValueColumn = "ObjectID"; } else { sKeyColumn = "ObjectID"; sValueColumn = "Value"; } // сформируем необходимый оператор delete на кросс-таблицу: StringBuilder cmdBuilder = new StringBuilder(); cmdBuilder.AppendFormat( "DELETE FROM {0} WHERE {1}={2}", sDBCrossTableName, // 0 xs.ArrangeSqlName(sKeyColumn), // 1 xs.ArrangeSqlGuid(xobj.ObjectID) // 2 //xs.GetParameterName("ObjectID") // 2 ); // если есть новые значения свойства, то исключим их из удаления if (values.Length > 0) { cmdBuilder.AppendFormat( " AND NOT {0} IN (", xs.ArrangeSqlName(sValueColumn) ); foreach (Guid oid in values) { cmdBuilder.Append(xs.ArrangeSqlGuid(oid)); cmdBuilder.Append(","); } // удалим последнюю запятую cmdBuilder.Length--; cmdBuilder.Append(")"); } disp.DispatchStatement( cmdBuilder.ToString(), //new XDbParameter[] {xs.CreateCommand().CreateParameter("ObjectID", DbType.Guid, ParameterDirection.Input, false, xobj.ObjectID)}, false ); // для каждого значения массивного свойства добавим INSERT в кросс-таблицу foreach (Guid value in values) { cmdBuilder.Length = 0; cmdBuilder.AppendFormat("INSERT INTO {0} ({1}, {2}) SELECT {3}, {4} WHERE NOT EXISTS (SELECT 1 FROM {0} WHERE {1}={3} AND {2}={4})", sDBCrossTableName, // 0 xs.ArrangeSqlName(sKeyColumn), // 1 xs.ArrangeSqlName(sValueColumn), // 2 xs.ArrangeSqlGuid(xobj.ObjectID), // 3 xs.ArrangeSqlGuid(value) // 4 ); disp.DispatchStatement(cmdBuilder.ToString(), false); } } } }
/// <summary> /// Формирует заготовку команды update для переданного объекта. /// Формируется текст ADO-команда, параметры и подсчитывается размер команды. /// Имя параметра устанавливается как имя колонки + "t" + nBatch + "o" + nIndex /// Для всех колонок используются параметры (в отличии от createUpdateCommandForSameTypeObjects). /// </summary> /// <param name="xs">Реализация XStorageConnection</param> /// <param name="disp">диспетчер запросов</param> /// <param name="xobj">объект, для которого требуется сформировать insert-команду</param> /// <param name="nBatchIndex">индекс группы объектов</param> /// <param name="nIndex">индекс объекта в списке</param> /// <param name="cmd">команда, как фабрика параметров</param> /// <param name="bSuppressMagicBit">признак того, что надо исключить обработку поля MagicBit. /// Если передан false, то в команде insert добавляется поле MagicBit устанавливаемое в 1. /// Если передан true, то в команде insert поле MagicBit не участвует. /// </param> /// <returns>заготовка команды с оператором UPDATE всех объектов из списка, либо null</returns> protected bool updateObject(XStorageConnection xs, XDbStatementDispatcher disp, XStorageObjectToSave xobj, int nBatchIndex, int nIndex, XDbCommand cmd, bool bSuppressMagicBit) { StringBuilder cmdBuilder; // построитель текста одной команды update string sPropName; // наименование свойства, колонки и параметра string sParamName; // наименование параметра команды object vValue; // значение свойства List <XDbParameter> aParameters = new List <XDbParameter>(); // коллекция параметров формируемой команды bool bCmdConstructed = false; // признак того, что команда update сформирована cmdBuilder = new StringBuilder(); cmdBuilder.AppendFormat("UPDATE {0} SET ", xs.GetTableQName(xobj.SchemaName, xobj.ObjectType)); if (xobj.UpdateTS) { cmdBuilder.AppendFormat("{0} = CASE WHEN {0}<{1} THEN {0}+1 ELSE 1 END{2}", xs.ArrangeSqlName("ts"), // 0 Int64.MaxValue, // 1 xs.Behavior.SqlNewLine); // 2 bCmdConstructed = true; } foreach (DictionaryEntry propDesc in xobj.Props) { sPropName = (String)propDesc.Key; vValue = propDesc.Value; XPropInfoBase propInfo = xobj.TypeInfo.GetProp(sPropName); if (!(propInfo is IXPropInfoScalar)) { continue; } if ((propInfo.VarType == XPropType.vt_bin || propInfo.VarType == XPropType.vt_text) /*&& vValue != DBNull.Value */) { continue; } if (bCmdConstructed) { cmdBuilder.Append(","); // текущая колонка уже не первая - добавим запятую } bCmdConstructed = true; xobj.TypeInfo.CheckPropValue(sPropName, propInfo.VarType, vValue); sParamName = xs.GetParameterName(String.Format("{0}t{1}o{2}", sPropName, nBatchIndex, nIndex)); cmdBuilder.Append(xs.ArrangeSqlName(sPropName) + "=" + sParamName + xs.Behavior.SqlNewLine); aParameters.Add(cmd.CreateParameter(sParamName, propInfo.VarType, ParameterDirection.Input, true, vValue)); } if (!bCmdConstructed) { return(false); } // если объект участвует в уникальных индексах и есть объекты в списках на вставку и/или удаление, то // установим MagicBit в 1 для предотвражения нарушения уникальных индексов if (!bSuppressMagicBit && xobj.ParticipateInUniqueIndex) { xobj.MagicBitAffected = true; cmdBuilder.AppendFormat(", {0}=1", xs.ArrangeSqlName("MagicBit")); } // сформируем условие WHERE: (ObjectID={@oid} AND ts={@ts}), // однако условие AND ts={@ts} добавим только если у объекта установлен признак AnalizeTS cmdBuilder.Append(" WHERE "); sParamName = xs.GetParameterName(String.Format("{0}t{1}o{2}", "ObjectID", nBatchIndex, nIndex)); cmdBuilder.AppendFormat("({0}={1}", xs.ArrangeSqlName("ObjectID"), sParamName); aParameters.Add(cmd.CreateParameter(sParamName, DbType.Guid, ParameterDirection.Input, true, xobj.ObjectID)); if (xobj.AnalyzeTS) { sParamName = xs.GetParameterName(String.Format("{0}t{1}o{2}", "ts", nBatchIndex, nIndex)); cmdBuilder.AppendFormat(" AND {0}={1}", xs.ArrangeSqlName("ts"), // 0 sParamName); // 1 aParameters.Add(cmd.CreateParameter(sParamName, DbType.Int64, ParameterDirection.Input, true, xobj.TS)); } cmdBuilder.Append(")"); disp.DispatchStatement(cmdBuilder.ToString(), aParameters, true); return(true); }
/// <summary> /// Формирует заготовку команды insert для переданного объекта. /// Формирутеся текст ADO-команда, параметры и подсчитывается размер команды. /// Имя параметра устанавливается как имя колонки + индекс объекта в общем списке /// </summary> /// <param name="xs">Реализация XStorageConnection</param> /// <param name="disp">диспетчер запросов</param> /// <param name="xobj">объект, для которого требуется сформировать insert-команду</param> /// <param name="cmd">команда</param> /// <param name="nIndex">индекс объекта в списке</param> /// <param name="bSuppressMagicBit">признак того, что надо исключить обработку поля MagicBit. /// Если передан false, то в команде insert добавляется поле MagicBit устанавливаемое в 1. /// Если передан true, то в команде insert поле MagicBit не участвует. /// </param> protected void insertObject(XStorageConnection xs, XDbStatementDispatcher disp, XStorageObjectToSave xobj, XDbCommand cmd, int nIndex, bool bSuppressMagicBit) { StringBuilder queryBuilder = new StringBuilder(); // построитель отдельного insert'a StringBuilder valuesBuilder = new StringBuilder(); // построитель списка значений string sPropName; // наименование свойства, колонки и параметра string sParamName; // наименование параметра команды object vValue; // значение свойства List <XDbParameter> Params = new List <XDbParameter>(); queryBuilder.AppendFormat("INSERT INTO {0} ({1}, {2}", xs.GetTableQName(xobj.SchemaName, xobj.ObjectType), // 0 xs.ArrangeSqlName("ObjectID"), // 1 xs.ArrangeSqlName("ts") // 2 ); // установим значения ObjectID, ts (в качастве ts установим 1) valuesBuilder.Append(xs.ArrangeSqlGuid(xobj.ObjectID) + ",1"); // если не запрещено, то значение MagicBit = 1 if (!bSuppressMagicBit && xobj.ParticipateInUniqueIndex) { queryBuilder.AppendFormat(",{0}", xs.ArrangeSqlName("MagicBit")); xobj.MagicBitAffected = true; valuesBuilder.Append(",1"); } foreach (DictionaryEntry propDesc in xobj.Props) { sPropName = (String)propDesc.Key; vValue = propDesc.Value; if (vValue == null) { continue; } XPropInfoBase propInfo = xobj.TypeInfo.GetProp(sPropName); if (!(propInfo is IXPropInfoScalar)) { continue; } if ((propInfo.VarType == XPropType.vt_bin || propInfo.VarType == XPropType.vt_text) /* && vValue != DBNull.Value*/) { continue; } xobj.TypeInfo.CheckPropValue(sPropName, propInfo.VarType, vValue); if (vValue != DBNull.Value) { queryBuilder.Append(','); queryBuilder.Append(xs.ArrangeSqlName(sPropName)); valuesBuilder.Append(','); if (xs.DangerXmlTypes.ContainsKey(propInfo.VarType)) { // сформируем наименование параметра (без префикса) как имя колонки + "o" + переданный индекс sParamName = xs.GetParameterName(sPropName + "o" + nIndex); Params.Add(cmd.CreateParameter(sParamName, propInfo.VarType, ParameterDirection.Input, false, vValue)); valuesBuilder.Append(sParamName); } else { valuesBuilder.Append(xs.ArrangeSqlValue(vValue, propInfo.VarType)); } } } // сформируем команду и добавим ее в общий батч queryBuilder.AppendFormat(") values ({0})", valuesBuilder.ToString()); disp.DispatchStatement(queryBuilder.ToString(), Params, false); }
/// <summary> /// Добавление объекта в коллекцию вставляемых объектов /// </summary> /// <param name="xobj"></param> public void AddInserted(XStorageObjectToSave xobj) { m_aObjectsToInsert.Add(xobj); m_hashObjectsToInsert.Add(getKey(xobj), xobj); }