Я работаю над переписыванием приложения, которое мы используем для запуска обновлений в нашей базе данных.По сути, идея приложения заключается в том, что оно берет набор сценариев (по одному для каждой версии) и запускает каждый сценарий между текущей версией базы данных и самой последней версией, которая у нее есть.Я пытаюсь найти лучший способ для нас справиться с этим процессом, и я пытался заставить объекты управления SQL работать так, как они нам нужны.Для справки, вот ограничения, с которыми я должен работать.
- Он должен обрабатывать операторы GO (что делает SMO)
- Он не может требовать от нас изменения наших файлов.(Это будет использоваться с сотнями, может быть, тысячами файлов, и нам не нужно редактировать каждый из них вручную, поэтому добавление ловушек попыток вроде бы исключено)
- Это должно продолжатьсяк следующему оператору GO, если он обнаружит ошибку.Это в основном соответствует тому, как работает наше текущее приложение.Если в одном из пакетов скрипта обнаружена ошибка, мы хотим, чтобы она перешла к следующему, поскольку они большую часть времени не связаны.
- Если скрипт встречает ошибку, он должен вывестисообщение об ошибке, чтобы пользователь мог знать, что обновление версии не сработало, и поэтому разработчики могут исправить ошибку для следующей версии (вот проблема)
Вот то, что у меня сейчас есть в виде кода:
string messages = "";
private void button1_Click(object sender, EventArgs e)
{
string setup = File.ReadAllText(@"[redacted]\Setup.sql");
string script = File.ReadAllText(@"[redacted]\6.3.6002.0.sql");
string script2 = File.ReadAllText(@"[redacted]\6.3.6003.0.sql");
var cnx = new SqlConnection(/*proper connection string*/);
var server = new Server(new ServerConnection(cnx));
//server.ConnectionContext.InfoMessage += ConnectionContext_InfoMessage;
server.ConnectionContext.ServerMessage += ConnectionContext_ServerMessage;
server.ConnectionContext.ExecuteNonQuery(setup);
server.ConnectionContext.ExecuteNonQuery(script);
server.ConnectionContext.ExecuteNonQuery(script2, ExecutionTypes.ContinueOnError);
txtResult.Text = messages;
}
private void ConnectionContext_ServerMessage(object sender, ServerMessageEventArgs e)
{
messages += e.Error.Message + "\r\n";
}
А вот сценарии, которые я использую:
Setup.sql:
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'UPGRADE_HISTORY')
DROP TABLE UPGRADE_HISTORY
IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = N'TEST_CODE_TABLE')
DROP TABLE TEST_CODE_TABLE
CREATE TABLE UPGRADE_HISTORY (
UPDATE_DATE DATE NOT NULL,
VERSION_TXT VARCHAR(50) NOT NULL,
PRIMARY KEY (UPDATE_DATE, VERSION_TXT)
)
CREATE TABLE TEST_CODE_TABLE (
CODE_VALUE INT PRIMARY KEY,
DESCRIPTION_TXT VARCHAR(250) NOT NULL
)
INSERT INTO UPGRADE_HISTORY VALUES
(DATEADD(d, -3, GETDATE()), '6.2.5000'),
(DATEADD(d, -1, GETDATE()), '6.2.5001'),
(DATEADD(d, -1, GETDATE()), '6.2.5002'),
(DATEADD(d, -1, GETDATE()), '6.3.6000.0'),
(DATEADD(d, -1, GETDATE()), '6.3.6001.0')
INSERT INTO TEST_CODE_TABLE VALUES
(1001, 'Test Code Table'),
(1002, 'Test Code Table 2')
6.3.6602.0.sql:
INSERT INTO UPGRADE_HISTORY VALUES
(GETDATE(), '6.3.6001.0')
GO
PRINT 'Test Code Table Change'
GO
UPDATE TEST_CODE_TABLE SET DESCRIPTION_TXT = 'Test Code Table Change' WHERE CODE_VALUE = 1002;
GO
6.3.6003.0.sql:
INSERT INTO UPGRADE_HISTORY VALUES
(GETDATE(), '6.3.6003.0')
GO
PRINT 'Test Error'
GO
INSERT INTO CODE_TABLE VALUES (1001, 'Test')
--This will throw an error since this will conflict with the primary key
--of the code table (or you know, because I just noticed it doesn't call
--the right table, it's really relevant since I want it to throw an
--error, w/e what it is)
GO
PRINT 'Second Test Code Table Change'
GO
UPDATE TEST_CODE_TABLE SET DESCRIPTION_TXT = 'Test Code Table Change 2' WHERE CODE_VALUE = 1002;
--We still want this to execute.
GO
Воспроизвести ситуацию, которая может возникнуть в наших обновлениях.Таким образом, как таковая, настройка заключается только в том, чтобы создавать заново базу данных каждый раз, чтобы я мог использовать одни и те же сценарии, затем первый файл обновления должен имитировать функционирование как предназначенный файл, а затем, наконец, 2-й файл обновления должен имитировать обновлениефайл с ошибкой.И вот тут начинаются проблемы.Как я понял, в тот момент, когда выполняется второй сценарий, запускается первая часть, затем вторая и выполняется ошибка, но я не получаю сообщение об ошибке.Ни события InfoMessage
, ни события ServerMessage
не запускаются.Затем запускается третья часть (та, что после оператора, который выдает ошибки), и я получаю ServerMessage для печати.Для справки вот вывод, который я получаю:
Test Code Table Change
Test Error
Second Test Code Table Change
Печать до и после возникновения ошибок, и я могу подтвердить с помощью двойной проверки данных, что оператор UPDATE
после ошибки также выполняется,Тем не менее, никакое сообщение или ошибка не выдается за тот факт, что оператор INSERT
выдает ошибку.Нам действительно нужно, чтобы SMO выдавала ошибку, или запускала событие ServerMessage
, или что-то действительно.Я что-то упускаю или это недостаток фреймворка.