Ejemplo n.º 1
0
        public override void Execute(XTriggerArgs args, IXExecutionContext context)
        {
            DomainObjectData xobj = args.TriggeredObject;
            // если изменилось значение признака "Архивное", проверим отсутствие у подразделения работающих сотрудников или не архивных департаментов.
            bool bUpdateIsArchive = xobj.HasUpdatedProp("IsArchive");

            if (bUpdateIsArchive)
            {
                bool newValue = (bool)xobj.GetUpdatedPropValue("IsArchive");
                if (!xobj.IsNew && newValue)
                {
                    //	1. Проверим, что все сотрудники (во всех вложенных департаментах) уволены
                    XDbCommand cmd = context.Connection.CreateCommand(@"
							SELECT 1 
							FROM dbo.Department d_s WITH(NOLOCK)
								JOIN dbo.Department d WITH(NOLOCK) ON d.LIndex >= d_s.LIndex AND d.RIndex <= d_s.RIndex AND d.Organization = d_s.Organization
									JOIN Employee e WITH(NOLOCK) ON (d.ObjectID = e.Department) and (e.WorkEndDate is null)
							WHERE d_s.ObjectID = @ObjectID
							"                            );
                    cmd.Parameters.Add("ObjectID", DbType.Guid, ParameterDirection.Input, false, xobj.ObjectID);
                    if (cmd.ExecuteScalar() != null)
                    {
                        throw new XBusinessLogicException("Департамент не может быть переведен с состояние \"Архивное\", так как содержит работающих сотрудников");
                    }
                }
                // добавим в датаграмму подчиненные департаменты
                UpdateChildDepartments(context.Connection, args.DataSet, xobj.ObjectID, newValue);
            }
        }
Ejemplo n.º 2
0
        protected Guid GetScalarObjectPropValueFromDB(XStorageConnection con, string sObjectType, Guid ObjectID, string[] aPathParts, int nStartIndex)
        {
            string sObjectType_cur = sObjectType;
            Guid   oid_cur         = ObjectID;
            string sQuery          = "@ObjectID";
            object vPropValue;                                                                          // значение свойства

            for (int nIndex = nStartIndex; nIndex < aPathParts.Length; ++nIndex)
            {
                sQuery = String.Format("SELECT {0} FROM {1} WHERE ObjectID = ({2})",
                                       con.ArrangeSqlName(aPathParts[nIndex]),  // 0 - свойство-колонка
                                       con.GetTableQName(sObjectType_cur),      // 1 - тип-таблица
                                       sQuery                                   // 2 - вложенное условие
                                       );
                sObjectType_cur = getObjectValueTypeName(sObjectType_cur, aPathParts[nIndex], con);
            }
            XDbCommand cmd = con.CreateCommand(sQuery);

            cmd.Parameters.Add("ObjectID", DbType.Guid, ParameterDirection.Input, false, oid_cur);
            vPropValue = cmd.ExecuteScalar();
            if (vPropValue == null)
            {
                return(Guid.Empty);
            }
            else if (vPropValue is DBNull)
            {
                return(Guid.Empty);
            }
            return((Guid)vPropValue);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Команда получения данных для фильтра списка тендеров
        /// </summary>
        public GetFilterTendersInfoResponse Execute(GetFilterTendersInfoRequest request, IXExecutionContext context)
        {
            GetFilterTendersInfoResponse resp = new GetFilterTendersInfoResponse();

            using (XDbCommand cmd = context.Connection.CreateCommand())
            {
                // Если идентификатор тендера не задан
                if (request.SelectedTenderID == Guid.Empty)
                {
                    cmd.CommandText =
                        @"SELECT TOP 1 ObjectID
						FROM dbo.Organization with (nolock)
						WHERE Home <> 0"                        ;
                    object temp = cmd.ExecuteScalar();
                    resp.OrganizationID = (temp == null) ?
                                          Guid.Empty : (Guid)temp;
                }
                // Если задан
                else
                {
                    cmd.CommandText =
                        @"SELECT TOP 1 O.ObjectID, T.DocFeedingDate
						FROM dbo.Tender AS T with (nolock)
						LEFT JOIN dbo.Lot AS L with (nolock) ON L.Tender = T.ObjectID
						LEFT JOIN dbo.LotParticipant AS P with (nolock) ON P.Lot = L.ObjectID
						LEFT JOIN dbo.Organization AS O with (nolock) ON O.ObjectID = P.ParticipantOrganization
						WHERE O.OwnTenderParticipant <> 0
							AND T.ObjectID = @SelectedTenderID"                            ;
                    // Передадим в параметр ID тендера
                    cmd.Parameters.Add("SelectedTenderID",
                                       DbType.Guid,
                                       ParameterDirection.Input,
                                       false,
                                       request.SelectedTenderID);

                    using (IDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.Read())
                        {
                            // Проставим значения ID огранизации и "даты подачи документов"
                            resp.OrganizationID = reader.IsDBNull(0) ?
                                                  Guid.Empty : reader.GetGuid(0);
                            resp.DocFeedingDate = reader.IsDBNull(1) ?
                                                  DateTime.MinValue : reader.GetDateTime(1);
                        }
                        else
                        {
                            resp.OrganizationID = Guid.Empty;
                            resp.DocFeedingDate = DateTime.MinValue;
                        }
                    }
                }
            }

            return(resp);
        }
Ejemplo n.º 4
0
        public override void Execute(XTriggerArgs args, Croc.XmlFramework.Core.IXExecutionContext context)
        {
            DomainObjectData xobj            = args.TriggeredObject;
            bool             bUpdatedState   = xobj.HasUpdatedProp("State");
            bool             bUpdateIsLocked = xobj.HasUpdatedProp("IsLocked");

            if (!bUpdatedState && !bUpdateIsLocked)
            {
                return;
            }
            // если здесь значит изменилось хотя бы одно из полей: Состояние (State), Дата блокирования списаний (BlockDate)
            // Теперь надо зачитать предыдущие значения из БД, но только тех свойств, которые обновляются
            preloadProps(xobj, bUpdatedState, bUpdateIsLocked, context);
            if (bUpdateIsLocked)
            {
                bool oldValue = (bool)xobj.GetLoadedPropValue("IsLocked");
                bool newValue = (bool)xobj.GetUpdatedPropValue("IsLocked");
                if (oldValue != newValue)
                {
                    // изменение признака допустимости списания
                    DomainObjectData xobjHistory = getFolderHistoryObject(args.DataSet, xobj);
                    xobjHistory.SetUpdatedPropValue("Event", newValue?FolderHistoryEvents.IsLockedSetToTrue:FolderHistoryEvents.IsLockedSetToFalse);
                }
            }

            if (bUpdatedState)
            {
                FolderStates stateOld = (FolderStates)xobj.GetLoadedPropValue("State");
                FolderStates stateNew = (FolderStates)xobj.GetUpdatedPropValue("State");
                if (stateOld != stateNew)
                {
                    // состояние изменилось
                    //	- проверим на запрещенные переходы
                    checkFolderStateChanging(stateOld, stateNew);
                    DomainObjectData    xobjHistory = getFolderHistoryObject(args.DataSet, xobj);
                    FolderHistoryEvents eventType;
                    if (stateNew == FolderStates.Closed)
                    {
                        eventType = FolderHistoryEvents.Closing;
                    }
                    else if (stateNew == FolderStates.Frozen)
                    {
                        eventType = FolderHistoryEvents.Frozing;
                    }
                    else if (stateNew == FolderStates.WaitingToClose)
                    {
                        eventType = FolderHistoryEvents.WaitingToClose;
                    }
                    else                        // if (stateNew == FolderStates.Open)
                    {
                        eventType = FolderHistoryEvents.Opening;
                    }
                    xobjHistory.SetUpdatedPropValue("Event", eventType);

                    // обработаем переход в состояние "Закрыто":
                    if (!xobj.IsNew && (stateNew == FolderStates.Closed || stateNew == FolderStates.WaitingToClose))
                    {
                        //	1. Проверим, что все инциденты (во всех вложенных папках) находятся в конечных состояниях
                        XDbCommand cmd = context.Connection.CreateCommand(@"
							SELECT 1 
							FROM Folder f_s WITH(NOLOCK)
								JOIN Folder f WITH(NOLOCK) ON f.LIndex >= f_s.LIndex AND f.RIndex <= f_s.RIndex AND f.Customer = f_s.Customer
									JOIN Incident i WITH(NOLOCK) ON f.ObjectID = i.Folder
										JOIN IncidentState i_st WITH(NOLOCK) ON i.State = i_st.ObjectID AND i_st.Category IN (1,2)	
							WHERE f_s.ObjectID = @ObjectID
							"                            );
                        cmd.Parameters.Add("ObjectID", DbType.Guid, ParameterDirection.Input, false, xobj.ObjectID);
                        if (cmd.ExecuteScalar() != null)
                        {
                            throw new XBusinessLogicException("Папка не может быть переведена в состояние \"Закрыто\" или \"Ожидание закрытия\", так как содержит незавершенные инциденты");
                        }
                    }

                    // добавим в датаграмму подчиненные папки с установленным состоянием новым состояние
                    // Обработка папок зависит от нового состояния
                    fillDataSetWithChildFoldersWithUpdatedState(context.Connection, args.DataSet, xobj.ObjectID, stateNew);
                }
            }
        }
Ejemplo n.º 5
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);
        }
Ejemplo n.º 6
0
        public XResponse Execute(SaveObjectInternalRequest request, IXExecutionContext context)
        {
            ITUser    user     = (ITUser)XSecurityManager.Instance.GetCurrentUser();
            ArrayList aObjects = request.DataSet.GetModifiedObjectsByType("TimeLoss", true);

            foreach (DomainObjectData xobj in aObjects)
            {
                if (!xobj.HasUpdatedProp("Worker") && xobj.IsNew)
                {
                    xobj.SetUpdatedPropValue("Worker", user.EmployeeID);
                }

                // получим папку, проверим у папки отсутствие флага IsLocked
                object           vValue     = xobj.GetPropValue("Folder", DomainObjectDataSetWalkingStrategies.UseUpdatedPropsThanLoadedProps);
                DomainObjectData xobjFolder = null;
                if (vValue is Guid)
                {
                    xobjFolder = xobj.Context.Get(context.Connection, xobj, "Folder", DomainObjectDataSetWalkingStrategies.UseUpdatedPropsThanLoadedProps, true);
                    // проверим состояние папки. если она закрыта или в ожидании закрытия, то запретим списание

                    if ((FolderStates)xobjFolder.GetLoadedPropValue("State") == FolderStates.Closed ||
                        (FolderStates)xobjFolder.GetLoadedPropValue("State") == FolderStates.WaitingToClose ||
                        (FolderStates)xobjFolder.GetLoadedPropValue("State") == FolderStates.Frozen)
                    {
                        throw new XSecurityException("Списания в папку в состоянии \"Закрыто\" или \"Ожидание закрытия\" запрещены. По вопросам списаний обращайтесь к менеджеру.");
                    }
                    // проверим наличие у папки специального аттрибута, запрещающего списание
                    if ((bool)xobjFolder.GetPropValue("IsLocked", DomainObjectDataSetWalkingStrategies.UseUpdatedPropsThanLoadedProps))
                    {
                        throw new XSecurityException("<b>Списания в данную папку запрещены.</b><br/>По вопросам списаний обращайтесь к менеджеру.");
                    }
                    FolderPrivilegeManager manager = (XSecurityManager.Instance.SecurityProvider as SecurityProvider).FolderPrivilegeManager;
                    // у пользователя должны быть права на списание в данной папке
                    if (!manager.HasFolderPrivilege(user, FolderPrivileges.SpentTimeByProject, xobjFolder, context.Connection))
                    {
                        throw new XSecurityException("<b>Пользователь должен обладать в проекте привилегией \"Списание времение на проект\".</b><br/>По вопросам списаний обращайтесь к менеджеру.");
                    }

                    // если у пользователя нет привилегии «Разрешение списания на папку с неоднозначным определением направления»
                    // проверим - является ли данная папка папкой с неоднозначным определением направления
                    if (!manager.HasFolderPrivilege(user, FolderPrivileges.TimeLossOnUnspecifiedDirection, xobjFolder, context.Connection))
                    {
                        using (XDbCommand c = context.Connection.CreateCommand())
                        {
                            c.CommandType = CommandType.Text;
                            c.CommandText = @"
IF	(
		SELECT t.AccountRelated 
		FROM dbo.ActivityType t WITH(NOLOCK) JOIN dbo.Folder fT WITH(NOLOCK) ON fT.ActivityType = t.ObjectID 
		WHERE fT.ObjectID = @FolderID
	) = 0
	SELECT 1
ELSE
	SELECT TOP 1 t.DirsQnt
	FROM (
		SELECT
			fU.ObjectID, fU.LRLevel,
			( SELECT COUNT(*) FROM dbo.FolderDirection fd WITH(NOLOCK) WHERE fd.Folder = fU.ObjectID ) AS DirsQnt
		FROM
			dbo.Folder fT WITH(NOLOCK)
			JOIN dbo.Folder fU WITH(NOLOCK) ON fU.Customer = fT.Customer AND fU.LIndex <= fT.LIndex AND fU.RIndex >= fT.RIndex
		WHERE
			fT.ObjectID = @FolderID
	) t
	WHERE DirsQnt > 0		
	ORDER BY LRLevel DESC 
";
                            c.Parameters.Add("FolderID", DbType.Guid, ParameterDirection.Input, false, vValue);

                            object oResult = c.ExecuteScalar();
                            string sReport = null;

                            if (null == oResult || DBNull.Value == oResult)
                            {
                                sReport = "для которой не определено ни одного направления";
                            }
                            else if (1 != (int)oResult)
                            {
                                sReport = "для которой определено более одного направления";
                            }

                            if (null != sReport)
                            {
                                throw new XSecurityException(
                                          "<b>Списания в данную папку запрещены.</b><br/>" +
                                          "У Вас нет прав списывать в папку, " + sReport + ". " +
                                          "По вопросам списаний <b>обращайтесь к менеджеру</b>.");
                            }
                        }
                    }
                }


                // если в объекте заданы "виртуальные" свойства
                if (xobj.HasUpdatedProp("LossFixedStart") && xobj.GetUpdatedPropValue("LossFixedStart") is DateTime &&
                    xobj.HasUpdatedProp("LossFixedEnd") && xobj.GetUpdatedPropValue("LossFixedEnd") is DateTime)
                {
                    if (!xobj.IsNew)
                    {
                        throw new ApplicationException("Задание диапазона допустимо только при создании объекта \"Списание времени\"");
                    }
                    // получим даты начала и окончания периода
                    DateTime dtPeriodStart = (DateTime)xobj.GetUpdatedPropValue("LossFixedStart");
                    DateTime dtPeriodEnd   = (DateTime)xobj.GetUpdatedPropValue("LossFixedEnd");
                    if (dtPeriodStart > dtPeriodEnd)
                    {
                        DateTime dtTemp = dtPeriodEnd;
                        dtPeriodEnd   = dtPeriodStart;
                        dtPeriodStart = dtTemp;
                    }

                    Guid employeeID = (Guid)xobj.GetUpdatedPropValue("Worker");
                    // проверим, что в данном периоде отсутствуют списания
                    XDbCommand cmd = context.Connection.CreateCommand("");
                    cmd.CommandText = @"SELECT DISTINCT CONVERT(varchar, x.SpentDate, 104) FROM (
						SELECT ts.RegDate AS SpentDate
						FROM TimeSpent ts
							JOIN Task t ON ts.Task = t.ObjectID
						WHERE t.Worker = @EmployeeID 
							AND ts.RegDate >= @dtPeriodStart AND ts.RegDate < @dtPeriodEnd
						UNION
						SELECT ts.LossFixed AS SpentDate
						FROM TimeLoss ts WHERE ts.Worker = @EmployeeID
							AND ts.LossFixed >= @dtPeriodStart AND ts.LossFixed < @dtPeriodEnd
						) x"                        ;
                    cmd.Parameters.Add("EmployeeID", DbType.Guid, ParameterDirection.Input, false, employeeID);
                    cmd.Parameters.Add("dtPeriodStart", DbType.Date, ParameterDirection.Input, false, dtPeriodStart);
                    // AddDays(1) - т.к. в условии запроса знак "меньше"
                    cmd.Parameters.Add("dtPeriodEnd", DbType.Date, ParameterDirection.Input, false, dtPeriodEnd.AddDays(1));
                    using (IDataReader reader = cmd.ExecuteReader())
                    {
                        if (reader.Read())
                        {
                            StringBuilder bld = new StringBuilder();
                            do
                            {
                                if (bld.Length > 0)
                                {
                                    bld.Append(", ");
                                }
                                bld.Append(reader.GetString(0));
                            } while(reader.Read());
                            throw new XBusinessLogicException(
                                      String.Format("У вас уже имеются списания на следующие даты в заданном периоде ({0},{1}): {2}",
                                                    dtPeriodStart.ToShortDateString(),
                                                    dtPeriodEnd.ToShortDateString(),
                                                    bld.ToString())
                                      );
                        }
                    }
                    // Получаем "набор пар" дата - норма сотрудника на дату
                    Dictionary <DateTime, int> dictDateRates = new Dictionary <DateTime, int>();
                    dictDateRates = GetDayRates(context.Connection, dtPeriodStart, dtPeriodEnd, employeeID);
                    // пойдем по всем дням периода (dtPeriodEnd, dtPeriodStart)
                    // и для каждой даты создадим новый объект Списание времени с количеством времени равном рабочему дню
                    // При этом, будем игнорировать даты, попадающие на выходные/праздники
                    TimeSpan period = (dtPeriodEnd - dtPeriodStart);
                    for (int nOffSet = 0; nOffSet <= period.TotalDays; ++nOffSet)
                    {
                        int      nRate  = 0;
                        DateTime dtDate = dtPeriodStart.AddDays(nOffSet);
                        // Если удалось получить норму сотрудника на соотв. дату, то создадим объект
                        if (dictDateRates.TryGetValue(dtDate, out nRate))
                        {
                            // Если полученная норма больше 0, тогда создадим списание
                            if (nRate > 0)
                            {
                                DomainObjectData xobjNew = createTimeLossObject(request.DataSet, xobj, user);
                                xobjNew.SetUpdatedPropValue("LossFixed", dtDate);
                                // списание длиной в количество минут в рабочем дне
                                xobjNew.SetUpdatedPropValue("LostTime", nRate);
                            }
                        }
                    }
                    // исходный объект надо удалить
                    request.DataSet.Remove(xobj);
                }
            }

            XSecurityManager sec_man    = XSecurityManager.Instance;
            IEnumerator      enumerator = request.DataSet.GetModifiedObjectsEnumerator(false);
            DomainObjectData xobject;

            while (enumerator.MoveNext())
            {
                xobject = (DomainObjectData)enumerator.Current;
                if (xobject.ToDelete)
                {
                    sec_man.DemandDeleteObjectPrivilege(xobject);
                }
                else
                {
                    sec_man.DemandSaveObjectPrivilege(xobject);
                }
            }
            // #1: Запись данных
            XStorageGateway.Save(context, request.DataSet, request.TransactionID);
            // Специального результата операция не возвращает
            return(new XResponse());
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Формирует и выполняет явный SQL-запрос на получение значения свойства
        /// (столбца) ObjectID для указанного ds-типа, для экземпляра, заданного
        /// значениями своих параметров
        /// </summary>
        /// <param name="sRequiredTypeName">
        /// Наименование ds-типа, для которого формируется запрос
        /// </param>
        /// <param name="dictionaryParams">
        /// Хеш параметров; здесь в паре ключ пары - наименование параметра,
        /// значение пары - собственно значение параметра. Последний может быть
        /// типизированным значением, значением в стрковом представленнии.
        /// Технически допустимо значение в виде массива типизированных значений
        /// или их строковых представлений, но такие значения в данном случае
        /// неприменимы и приводят к генерации исключения ArgumentException.
        /// </param>
        /// <param name="connection">Соединение с СУБД; на момент вызова д.б. открыто</param>
        /// <returns>Значение ObjectID экземпляра объекта</returns>
        protected Guid processExplicitObjectIdRequest(
            string sRequiredTypeName,
            Hashtable dictionaryParams,
            XStorageConnection connection)
        {
            // SQL-операция получения значения ObjectID экземпляра заданного типа
            XDbCommand command = connection.CreateCommand();
            // Строка, в которой будем собирать WHERE-условие для SQL-операции
            StringBuilder sWhereClause = new StringBuilder();

            // #1: Формируем WHERE-условие; для этого перебираем все параметры,
            // переданные в составе коллекции, и для каждого параметра (а) добавляем
            // условие в WHERE-выражение, (б) добавляем соответствующий параметр в
            // коллекцию параметров SQL-операции. Наименование свойства, на которое
            // накладывается условие и наименование параметра формируются на основании
            // наименования парамтра, переданного в исходной коллекции; на всякий
            // пожарный случай к нарименованию параметра в SQL-выражении добавляем
            // префикс "param":
            foreach (DictionaryEntry item in dictionaryParams)
            {
                // В случае непосредственного запроса на получение ObjectID
                // массивные значения параметров недопустимы:
                if (item.Value is ArrayList || item.Value is Array)
                {
                    throw new ArgumentException(String.Format(
                                                    "В качестве значения параметра {0} передан массив значений, " +
                                                    "что недопустимо в случае явного запроса идентификатора объекта типа {1}",
                                                    item.Key.ToString(), sRequiredTypeName)
                                                );
                }

                // Если значение параметра задано как NULL, то это специальный случай
                // условия, обрабатываем его отдельно:
                if (null == item.Value)
                {
                    // Наименование свойства, на которое накладывается условие,
                    // есть наименования параметра, переданного в коллекции:
                    sWhereClause.AppendFormat(
                        "(obj.{0} IS NULL) AND ",
                        connection.Behavior.ArrangeSqlName(item.Key.ToString())
                        );
                }
                else
                {
                    // Наименование SQL-параметра: наименование параметра из
                    // в исходной коллекции, к которому добавлен префикс:
                    string sParamName = "param" + item.Key.ToString();

                    // Наименование свойства, на которое накладывается условие,
                    // есть наименования параметра из исходной коллекции:
                    sWhereClause.AppendFormat("(obj.{0}={1}{2}) AND ",
                                              connection.Behavior.ArrangeSqlName(item.Key.ToString()),          // 0
                                              connection.Behavior.ParameterPrefix,                              // 1
                                              sParamName                                                        // 2
                                              );

                    // Создаем объект-параметр, добавлем его в коллекцию параметров
                    // SQL-операции:
                    XDbParameter param = command.CreateParameter();
                    param.ParameterName = sParamName;
                    param.VarType       = XPropTypeParser.GetNearestTypeForCLR(item.Value.GetType());
                    param.Value         = item.Value;
                    // Если тип параметра есть строка - ограничим размерность,
                    // т.к. в противном случае это будет максимальная размерность - 4К -
                    // и будет достаточно заметно тормозить. В качестве исходной размерности
                    // укажем длину реально заданного значения, увеличенного на 2:
                    if (item.Value is string)
                    {
                        param.Size = ((string)item.Value).Length + 2;
                    }
                    command.Parameters.Add(param);
                }
            }
            sWhereClause.Append("(1=1)");


            // #2: Формируем и выполняем полную SQL-операцию
            command.CommandType = System.Data.CommandType.Text;
            command.CommandText = String.Format(
                "SELECT TOP 1 obj.ObjectID FROM {0} obj WHERE {1}",
                connection.GetTableQName(sRequiredTypeName),
                sWhereClause.ToString()
                );
            object oResult = command.ExecuteScalar();


            // #3: Ожидается, что в результате мы получаем GUID; если
            // результата нет вообще, возвращаем Guid.Empty;
            Guid uidResult = Guid.Empty;

            if (null != oResult && DBNull.Value != oResult)
            {
                uidResult = connection.Behavior.CastGuidValueFromDB(oResult);
            }

            return(uidResult);
        }