При разработке отчёта встала задача определения сколько дней в указанном месяце в указанный год (не забываем, что есть висакосные года, когда в феврале 29 дней). Долго чесать репу не пришлось и всё-таки решение на T-SQL достаточно простое. Ниже текст скалярной функции, которая вычисляет последний день указанного месяца, точнее по указанной дате возвращает дату конечного дня... в общем как-то так :)
---------------------------------------------------------------------------
CREATE FUNCTION GET_LASTDAY_MONTH
(
@DATE DATE
)
RETURNS DATE
AS
BEGIN
DECLARE @RESULT DATE
DECLARE @DAY_OF_MONTH TINYINT
DECLARE @TEMP_RESULT DATE
SET @DAY_OF_MONTH = DAY(DATEADD(MONTH, 1, CAST(@DATE AS DATETIME)) - DAY (DATEADD(MONTH, 1, @DATE)))
SET @TEMP_RESULT = DATEADD(DAY, (@DAY_OF_MONTH - DAY(@DATE)), @DATE)
SET @RESULT = @TEMP_RESULT
RETURN @RESULT
END
------------------------------------------------------------------------------------------
В всё просто :)
Пишу для того чтобы не забыть, да и возможно кому-то пригодиться.
среда, 18 мая 2011 г.
понедельник, 19 апреля 2010 г.
The operation has timed out in .NET and connectionManagement
Произошла одна интересная ситуация на одной высоконагруженных систем, над которой мне как-то пришлось работать.
Ситуация следующая:
Один из компонентов системы, который разработан на .NET 2.0 и является Windows сервисом, так же он работал в большое количество потоков, вдруг начинал выдать ошибки о таймауте выполнения операции.
Начали расследование... поставили снифер, оказалось, что соединения даже не открывались. Точнее всё было ещё более интересней: соединения сначала нормально открывались по всем хостам, далее на один хост, на который шло больше всего сообщений, количество соединений доходило примерно до 20, а затем начинали сыпаться ошибки и именно только по этому хосту по остальным всё было нормально.
Долго думали и курили Гуголь, в итоге на одном из забугорных форумов была найдена заметка о том, что майкрософт ограничивает по-умолчанию количество исходящих соединений на один хост... вот такая вот интересная фича.
В общем-то вопрос решался простой вставкой вот такого текста в конфигурационный файл компонента приложения:
Ситуация следующая:
Один из компонентов системы, который разработан на .NET 2.0 и является Windows сервисом, так же он работал в большое количество потоков, вдруг начинал выдать ошибки о таймауте выполнения операции.
Начали расследование... поставили снифер, оказалось, что соединения даже не открывались. Точнее всё было ещё более интересней: соединения сначала нормально открывались по всем хостам, далее на один хост, на который шло больше всего сообщений, количество соединений доходило примерно до 20, а затем начинали сыпаться ошибки и именно только по этому хосту по остальным всё было нормально.
Долго думали и курили Гуголь, в итоге на одном из забугорных форумов была найдена заметка о том, что майкрософт ограничивает по-умолчанию количество исходящих соединений на один хост... вот такая вот интересная фича.
В общем-то вопрос решался простой вставкой вот такого текста в конфигурационный файл компонента приложения:
<connectionManagement>
<add address="*" maxconnection="5000">
</connectionManagement>
Т.е. тут происходит указание, что всего может быть не более пяти тысяч исходящих подключений. Вообще почитав доки на MSDN можно понять, что тут достаточно шаблонированное управление количеством исходящих подключений и можно конкретно указать как домен, так и поддомен целиком.
понедельник, 1 марта 2010 г.
ConfigurationManager vs WebConfigurationManager
Проблема была в следующем:
Писалась библиотека, которая должна одинаково хорошо работать как в WebApp, так и в ClientApp. Настройка библиотеки для работы, можно было бы осуществлять, как непосредственно (через установку свойств статических классов), так и через *.config приложения. Всё бы ничего, только вот незадача для получения данных из конфига в WebApp используется класс WebConfigurationManager, а для ClientApp используется ConfigurationManager. В общем-то они делают реализацию для одного и того же абстрактного класса. Начал думать, как отличить в каком контексте работает приложение. Было придумано куча вариантов, включая получение пути к конфигурационному файлу (из System.AppDomain.CurrentDomain.SetupInformation.ConfigurationFile и не париться вообще в отличении контекстов) и разбор его вручную чтобы найти свою секцию и забрать из неё данные. Но всё это как-то не прозрачно, да и запутано. А потом я вспомнил про слово-оператор as.... и получилось достаточно компактно и понятно:
DataMapConfigSectionHandler dmcsh;
dmcsh = ConfigurationManager.GetSection("ntlDataMapConfig") as DataMapConfigSectionHandler;
if (dmcsh == null)
{
dmcsh = WebConfigurationManager.GetSection("ntlDataMapConfig") as DataMapConfigSectionHandler;
}
После этого нужно dmcsh проверить на null, так как dmcsh может равняться null, если секция, которую вы указали не найдена.
Правда, такой подход всё равно накладывает определённые нехорошести: классы ConfigurationManager и WebConfigurationManager находятся в разных сборках соответственно System.Configuration.dll и System.Web.dll. CLR придётся загрузить их обе, и соответственно отъесть немного памяти, если для вас это критично, то, наверное всё-таки придётся загружать вручную конфигурационный файл и парсить его ручками, как XML документ.
четверг, 25 февраля 2010 г.
Конвертирование между типами System.String и Enum - кастомным.
В общем-то давно не писал, некогда как-то писать и тут начал возиться с системой используемой в .NET для создания собственных секций файлах .config. Всё бы ничего, только вот некоторые данные, которые должны храниться в конфиге приложения в системе имеют тип Enum.
Самое простое просто написать два хелпера: первый будет состоять из кучи if-else, где последовательно будут сравнивать строки и на выход выкинуть необходимое enum значение. Второй хелпер должен был бы конвертить Enum в строку, опять же просто switch перебирающий значения перечисления и возвращающий строку.
Почесав репу и скрепяклавиатурой сердцем решил так и сделать... но тут понял, что switch-то понятно работает в нормально сборке достаточно шустро - на хешах, а вот перебор вариантов if-else всё-таки перебор и он как известно достаточно медленная вещь.
Погуглив и попробовав несколько вариантов получилась такая вот тема:
1) Для конвертирования System.String в Enum:
(EnumType)Enum.Parse(typeof (EnumType), somethingString, true);
2) Для конвертирования Enum в System.String:
Enum.GetName(typeof(EnumType), somethingString);
Пояснения:
EnumType - это какой-то определённый нами энумератор;
somethingString - строчка с названием одного из членов перечисления;
Самое простое просто написать два хелпера: первый будет состоять из кучи if-else, где последовательно будут сравнивать строки и на выход выкинуть необходимое enum значение. Второй хелпер должен был бы конвертить Enum в строку, опять же просто switch перебирающий значения перечисления и возвращающий строку.
Почесав репу и скрепя
Погуглив и попробовав несколько вариантов получилась такая вот тема:
1) Для конвертирования System.String в Enum:
(EnumType)Enum.Parse(typeof (EnumType), somethingString, true);
2) Для конвертирования Enum в System.String:
Enum.GetName(typeof(EnumType), somethingString);
Пояснения:
EnumType - это какой-то определённый нами энумератор;
somethingString - строчка с названием одного из членов перечисления;
пятница, 3 апреля 2009 г.
ClearQuest и .NET Часть 3
Ну что же продолжаем знакомиться с API ClearQuest.
Сегодня будем добавлять дефекты в его БД. Получилось два метода, хотя можно было уложить и в один. Собственно вот:
Public Sub AddDefect(ByVal defect As DefectObject)
Private Sub AddSectionsInDefect(ByVal entityObj As Object, ByVal secrions As IList(Of String))
Так... начнём, как обычно, с теории.
API предоставляет достаточно удобный интерфейс для создания и заведения дефектов.
В общем-то процесс добавления разбит на несколько стадий: Получение сессии, создание сущности, заполнение полей, валидация и сохранение в БД.
Получить сессию - думаю понятно, далее создаём сущность, в данном случае - это сущность дефекта, вот так Dim entityObj = sessionObj.BuildEntity("defect") . Заполняем поля - для этого существует специальный метод: SetFieldValue("SubProject", defect.SubProject) , на вход которому необходимо подать, соответственно, название поля и значение, которое необходимо присвоить. Так же существует способ заполнения списочных (коллекций) полей AddFieldValue("Section", tempStr) . Сигнатура метода такая же, но работает он как метод Add(объект) у коллекций во многих языках.
Далее самое интересное - валидация. Она нужна для того, чтобы проверить наш заполненный объект сущности на соответствие схеме базы данных. Так как обычно при развороте схемы появляются поля, которые должны быть заполнены в обязательном порядке, да и константы не кто не отменял. Так вот для валидации служит специальный метод у объекта сущности под названием Validate() . Он возвращает значение строкового типа, а так же меняет статус объект на "валидированный" и фишка в том, что только из этого статуса объект можно сохранить командой Commit() .
Так вот, метод Validate() возвращает строку. Если возвращенная строка пустая, то значит всё ОК, если нет значит нам возвращается описание проблемы, к тому же достаточно подробное. Т.е. это возвращаемое значение нужно как-то обрабатывать.
Всё... теперь, если валидация успешна, можно сохранять данные :) PS. метод Commit() .
В следующий раз расскажу как аттачить файлы.
Сегодня будем добавлять дефекты в его БД. Получилось два метода, хотя можно было уложить и в один. Собственно вот:
Public Sub AddDefect(ByVal defect As DefectObject)
Dim sessionObj = GetSession()End Sub
Dim entityObj = sessionObj.BuildEntity("defect")
'Обязательные поля
entityObj.SetFieldValue("SubProject", defect.SubProject)
AddSectionsInDefect(entityObj, defect.Section)
entityObj.SetFieldValue("Headline", defect.Headline)
entityObj.SetFieldValue("Owner", defect.Owner)
entityObj.SetFieldValue("Type", defect.Type)
entityObj.SetFieldValue("Severity", defect.Severity)
'================================================
entityObj.SetFieldValue("Description", defect.Description)
entityObj.SetFieldValue("FoundInBuild", defect.FoundInBuild)
entityObj.SetFieldValue("OriginalSource", defect.OriginalSource)
Dim status = entityObj.Validate()
If (status <> "") Then Throw New ArgumentException(status, "defect")
entityObj.Commit()
Private Sub AddSectionsInDefect(ByVal entityObj As Object, ByVal secrions As IList(Of String))
Dim tempStr As StringEnd Sub
For Each tempStr In secrions
entityObj.AddFieldValue("Section", tempStr)
Next tempStr
Так... начнём, как обычно, с теории.
API предоставляет достаточно удобный интерфейс для создания и заведения дефектов.
В общем-то процесс добавления разбит на несколько стадий: Получение сессии, создание сущности, заполнение полей, валидация и сохранение в БД.
Получить сессию - думаю понятно, далее создаём сущность, в данном случае - это сущность дефекта, вот так Dim entityObj = sessionObj.BuildEntity("defect") . Заполняем поля - для этого существует специальный метод: SetFieldValue("SubProject", defect.SubProject) , на вход которому необходимо подать, соответственно, название поля и значение, которое необходимо присвоить. Так же существует способ заполнения списочных (коллекций) полей AddFieldValue("Section", tempStr) . Сигнатура метода такая же, но работает он как метод Add(объект) у коллекций во многих языках.
Далее самое интересное - валидация. Она нужна для того, чтобы проверить наш заполненный объект сущности на соответствие схеме базы данных. Так как обычно при развороте схемы появляются поля, которые должны быть заполнены в обязательном порядке, да и константы не кто не отменял. Так вот для валидации служит специальный метод у объекта сущности под названием Validate() . Он возвращает значение строкового типа, а так же меняет статус объект на "валидированный" и фишка в том, что только из этого статуса объект можно сохранить командой Commit() .
Так вот, метод Validate() возвращает строку. Если возвращенная строка пустая, то значит всё ОК, если нет значит нам возвращается описание проблемы, к тому же достаточно подробное. Т.е. это возвращаемое значение нужно как-то обрабатывать.
Всё... теперь, если валидация успешна, можно сохранять данные :) PS. метод Commit() .
В следующий раз расскажу как аттачить файлы.
Подписаться на:
Сообщения (Atom)