Пример #1
0
        /// <summary>
        /// Процедура, формирующая операцию UPDATE с использованием данных из временной таблицы
        /// </summary>
        /// <param name="xs">Реализация XStorageConnection</param>
        /// <param name="disp">XDbStatementDispatcher</param>
        /// <param name="sTempTableName">Имя временной таблице (уже заэнкоженное)</param>
        /// <param name="bUseMagicBit">Признак использование "магического бита"</param>
        /// <param name="sTypeName">Имя типа обновляемой группы объектов</param>
        /// <param name="sSchemaName">Наименование схемы</param>
        /// <param name="xmlTypeMD">Метаданные типа</param>
        protected override void updateWithTempTable(
            XStorageConnection xs,
            XDbStatementDispatcher disp,
            string sTempTableName,
            bool bUseMagicBit,
            string sSchemaName,
            string sTypeName,
            XmlElement xmlTypeMD)
        {
            StringBuilder cmdBuilder;

            cmdBuilder = new StringBuilder();
            cmdBuilder.AppendFormat(
                "UPDATE {0} SET {1}{2} = CASE WHEN s.{3} ='1' THEN CASE WHEN d.{2}<{3} THEN d.{2}+1 ELSE 1 END ELSE d.{2} END{1}",
                xs.GetTableQName(sSchemaName, sTypeName),               // 0
                xs.Behavior.SqlNewLine,                                 // 1
                xs.ArrangeSqlName("ts"),                                // 2
                xs.ArrangeSqlName("x_ts"),                              // 3
                Int64.MaxValue                                          // 4
                );
            // Для каждого скалярного свойства update на значение из временной таблице, если в колонке x_{имя_свойства} лежит 1, иначе само на себя
            foreach (XmlElement xmlPropMD in xmlTypeMD.SelectNodes("ds:prop[@cp='scalar' and @vt!='bin' and @vt!='text']", xs.MetadataManager.NamespaceManager))
            {
                cmdBuilder.AppendFormat(",[{0}] = CASE WHEN s.[x{0}]='1' THEN s.[c{0}] ELSE d.[{0}] END{1}",
                                        xmlPropMD.GetAttribute("n"),    // 0
                                        xs.Behavior.SqlNewLine          // 1
                                        );
            }
            if (bUseMagicBit)
            {
                cmdBuilder.Append(",[MagicBit]=1" + xs.Behavior.SqlNewLine);
            }
            cmdBuilder.AppendFormat("FROM {0} d JOIN {1} s ON d.ObjectID = s.ObjectID AND (d.ts = s.ts OR s.ts IS NULL)",
                                    xs.GetTableQName(sSchemaName, sTypeName),   // 0
                                    sTempTableName                              // 1
                                    );
            disp.DispatchStatement(cmdBuilder.ToString(), true);
        }
Пример #2
0
        /// <summary>
        /// Выполняет удаление в БД объектов в списке в порядке их следования в нем
        /// </summary>
        /// <param name="xs">Экземпляр XStorageConection</param>
        /// <param name="disp">Диспетчер запросов</param>
        /// <param name="aDelObjects">список объектов (типа ObjectToDelete), для которых надо выполнить delete в БД</param>
        protected virtual int doDelete(XStorageConnection xs, XDbStatementDispatcher disp, ICollection aDelObjects)
        {
            int           nRowsAffected    = 0;                         // количество удаленных записей
            bool          bForcedMode      = false;                     // признак форсированного удаления (есть хотя бы один объект с незаданным ts)
            string        sTypeNamePrev    = String.Empty;              // наименование типа предыдущего объекта (в цикле)
            StringBuilder queryTextBuilder = new StringBuilder();       // построитель оператора delete


            if (aDelObjects.Count == 0)
            {
                return(0);
            }
            foreach (XStorageObjectToDelete obj in aDelObjects)
            {
                if (obj.TS == -1)
                {
                    bForcedMode = true;
                }
                if (sTypeNamePrev != obj.ObjectType)
                {
                    // объект нового типа (в т.ч. первый)
                    if (queryTextBuilder.Length > 0)
                    {
                        disp.DispatchStatement(queryTextBuilder.ToString(), true);
                        queryTextBuilder.Length = 0;
                    }
                    queryTextBuilder.AppendFormat("DELETE FROM {0} WHERE {1}={2}",
                                                  xs.GetTableQName(obj.TypeInfo),       // 0
                                                  xs.ArrangeSqlName("ObjectID"),        // 1
                                                  xs.ArrangeSqlGuid(obj.ObjectID)       // 2
                                                  );
                    // если для объекта задан TS, добавим условие на него
                    if (obj.AnalyzeTS)
                    {
                        queryTextBuilder.AppendFormat(" AND {0}={1}",
                                                      xs.ArrangeSqlName("ts"),
                                                      obj.TS
                                                      );
                    }
                }
                else
                {
                    // еще один объект того же типа
                    queryTextBuilder.AppendFormat(" OR {0}={1}",
                                                  xs.ArrangeSqlName("ObjectID"),        // 0
                                                  xs.ArrangeSqlGuid(obj.ObjectID)       // 1
                                                  );
                    // если для объекта задан TS, добавим условие на него
                    if (obj.AnalyzeTS)
                    {
                        queryTextBuilder.AppendFormat(" AND {0}={1}",
                                                      xs.ArrangeSqlName("ts"),
                                                      obj.TS
                                                      );
                    }
                }
                sTypeNamePrev = obj.ObjectType;
            }
            if (queryTextBuilder.Length > 0)
            {
                disp.DispatchStatement(queryTextBuilder.ToString(), true);
            }

            nRowsAffected = disp.ExecutePendingStatementsAndReturnTotalRowsAffected();

            if (!bForcedMode)
            {
                if (nRowsAffected != aDelObjects.Count)
                {
                    // количество удаленных объектов не совпадает с ожидаемым кол-вом.
                    // если в БД остался хотя бы один объект из тех, которые мы удалили, то
                    // это означает, что у него "устарел" ts, следовательно будем ругаться.
                    // Иначе (в БД нет ниодного объект из тех, которые мы удаляли) все хорошо, просто
                    // нас кто-то опередил, но главное результат - все требуемые объекты удалены.
                    sTypeNamePrev    = String.Empty;
                    queryTextBuilder = new StringBuilder();
                    XDbCommand cmd = xs.CreateCommand();
                    cmd.CommandType = CommandType.Text;
                    foreach (XStorageObjectToDelete obj in aDelObjects)
                    {
                        if (sTypeNamePrev != obj.ObjectType)
                        {
                            // объект нового типа (в т.ч. первый)
                            if (queryTextBuilder.Length > 0)
                            {
                                cmd.CommandText = queryTextBuilder.ToString();
                                if (Convert.ToInt32(cmd.ExecuteScalar()) > 0)
                                {
                                    throw new XOutdatedTimestampException();
                                }
                                queryTextBuilder.Length = 0;
                            }
                            queryTextBuilder.AppendFormat("SELECT COUNT(1) FROM {0} WHERE {1}={2}",
                                                          xs.GetTableQName(obj.TypeInfo),               // 0
                                                          xs.ArrangeSqlName("ObjectID"),                // 1
                                                          xs.ArrangeSqlGuid(obj.ObjectID)               // 2
                                                          );
                        }
                        else
                        {
                            // еще один объект того же типа
                            queryTextBuilder.AppendFormat(" OR {0}={1}",
                                                          xs.ArrangeSqlName("ObjectID"),                // 0
                                                          xs.ArrangeSqlGuid(obj.ObjectID)               // 1
                                                          );
                        }
                        sTypeNamePrev = obj.ObjectType;
                    }
                    cmd.CommandText = queryTextBuilder.ToString();
                    if (Convert.ToInt32(cmd.ExecuteScalar()) > 0)
                    {
                        throw new XOutdatedTimestampException();
                    }
                }
            }

            return(nRowsAffected);
        }
Пример #3
0
        /// <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);
                    }
                }
            }
        }
Пример #4
0
        /// <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
            }                   // конец цикла по объектам из списка обновляемых
        }
Пример #5
0
        /// <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);
        }
Пример #6
0
        /// <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);
        }