Я работал над написанием установщика SQL для своего приложения, и у меня не было много времени для его работы, поэтому он немного грязный. Мой поток пользовательского интерфейса собирает значения из формы, вставляет их в серию SQL-скриптов, а затем один за другим запускает их «асинхронно» с помощью SMO. Вот пример вызова метода в потоке пользовательского интерфейса:
private static bool BeginScriptExecution(Dictionary<string, string[]> scripts)
{
try
{
foreach (var script in scripts)
{
if (script.Key.Length > 0)
{
SqlConnection conn = new SqlConnection();
conn = ReplaceDatabaseName(GetConnectionString(), script.Value[0]);
if (TestSqlConnection())
{
if (ConfigurationManager.AppSettings["useSMO"] == "1")
{
if (!RunDatabaseCommandsSmo(conn, script.Key, script.Value[1]).Result)
{
throw new Exception("Script failed to run. Error in SMO functions.");
}
}
else
{
if (!RunDatabaseCommandsNonSmo(conn, script.Key, script.Value[1]))
{
throw new Exception("Script failed to run. Non-SMO related failure.");
}
}
}
else
{
throw new Exception("Connection not available. Script failed to run");
}
}
}
return true;
}
catch (Exception ex)
{
log.Error(ex.ToString());
return false;
}
}
Вот мой асинхронный метод для запуска команды с использованием SMO:
public static async Task<bool> RunDatabaseCommandsSmo(SqlConnection connectionString, string scriptText, string scriptName)
{
Helper.UpdateProgressLabel(scriptName);
bool isSuccess = false;
try
{
ServerConnection srvCon = new ServerConnection(connectionString);
Server server = new Server(srvCon);
//script = scriptText;
if (scriptName.Contains("Creating Database")||scriptName.Contains("Building objects"))
{
try
{
isSuccess = await Task.Run(() => RunCommand(scriptText, server))
.ConfigureAwait(continueOnCapturedContext:false);
return isSuccess;
}
catch(Exception ex)
{
log.Error("Cannot create database");
log.Error(ex.StackTrace.ToString());
return false;
}
}
else
{
try
{
//server.ConnectionContext.ExecuteNonQuery(scriptText);
isSuccess = await Task.Run(() => RunTransaction(scriptText, server, srvCon))
.ConfigureAwait(continueOnCapturedContext: false);
return isSuccess;
}
catch (Exception ex)
{
log.Error(string.Format("Error writing transaction from script {0}. Installation halted - check inner exception.", scriptName));
log.Error(ex.ToString());
log.Error(ex.StackTrace.ToString());
return false;
}
}
}
catch (Exception ex)
{
log.Error(string.Format("Error writing transaction from script {0}. Installation halted - check inner exception.", scriptName));
log.Error(ex.StackTrace.ToString());
return false;
}
}
Вот код, выполняющий транзакции:
static bool RunCommand(string script, Server server)
{
try
{
server.ConnectionContext.ExecuteNonQuery(script);
return true;
}
catch(Exception ex)
{
log.Error(ex.ToString());
return false;
}
}
static bool RunTransaction(string script, Server server, ServerConnection srvCon)
{
try
{
srvCon.BeginTransaction();
server.ConnectionContext.ExecuteNonQuery(script);
srvCon.CommitTransaction();
return true;
}
catch (Exception ex)
{
srvCon.RollBackTransaction();
log.Error(ex.ToString());
return false;
}
}
Прежде чем я начал двигаться в сторону асинхронности, я выполнял все в потоке пользовательского интерфейса, и форма выдала мне сообщение «Не отвечает», пока не вернется завершенный сценарий. Как только я перешел на этот стиль асинхронности, приложение стало более отзывчивым, и я больше не отвечал, но я не уверен в этом методе и в правильности использования асинхронности. Может кто-нибудь сообщить мне, как я могу изменить свой код, чтобы эта работа работала правильно? Существует 4 сценария, которые выполняются и должны запускаться в определенном порядке, что означает, что сценарий 2 не может быть запущен до тех пор, пока сценарий 1 не вернется.