Microsoft Robotics и Sql
У меня есть проблема, реализующая CCR с SQL. Кажется, что, когда я перебираю в своем коде обновления и вставки, я пытаюсь выполнить работу отлично. Но когда я запускаю свой интерфейс без каких-либо точек останова, он, кажется, работает и показывает вставки, обновления, но в конце выполнения ничего не обновляется в базе данных.
Я продолжал добавлять паузу в мой код каждый раз, когда я вытаскиваю новую нить из своего пула, и она работает... но это нарушает цель асинхронного кодирования, верно? Я хочу, чтобы мой интерфейс был быстрее, а не замедлен...
Любые предложения... вот часть моего кода:
Я использую два вспомогательных класса, чтобы установить свои порты и получить ответ...
/// <summary>
/// Gets the Reader, requires connection to be managed
/// </summary>
public static PortSet<Int32, Exception> GetReader(SqlCommand sqlCommand)
{
Port<Int32> portResponse = null;
Port<Exception> portException = null;
GetReaderResponse(sqlCommand, ref portResponse, ref portException);
return new PortSet<Int32, Exception>(portResponse, portException);
}
// Wrapper for SqlCommand's GetResponse
public static void GetReaderResponse(SqlCommand sqlCom,
ref Port<Int32> portResponse, ref Port<Exception> portException)
{
EnsurePortsExist(ref portResponse, ref portException);
sqlCom.BeginExecuteNonQuery(ApmResultToCcrResultFactory.Create(
portResponse, portException,
delegate(IAsyncResult ar) { return sqlCom.EndExecuteNonQuery(ar); }), null);
}
тогда я делаю что-то вроде этого, чтобы поставить в очередь мои звонки...
DispatcherQueue queue = CreateDispatcher();
String[] commands = new String[2];
Int32 result = 0;
commands[0] = "exec someupdateStoredProcedure";
commands[1] = "exec someInsertStoredProcedure '" + Settings.Default.RunDate.ToString() + "'";
for (Int32 i = 0; i < commands.Length; i++)
{
using (SqlConnection connSP = new SqlConnection(Settings.Default.nbfConn + ";MultipleActiveResultSets=true;Async=true"))
using (SqlCommand cmdSP = new SqlCommand())
{
connSP.Open();
cmdSP.Connection = connSP;
cmdSP.CommandTimeout = 150;
cmdSP.CommandText = "set arithabort on; " + commands[i];
Arbiter.Activate(queue, Arbiter.Choice(ApmToCcrAdapters.GetReader(cmdSP),
delegate(Int32 reader) { result = reader; },
delegate(Exception e) { result = 0; throw new Exception(e.Message); }));
}
}
где ApmToCcrAdapters - это имя класса, где мои вспомогательные методы...
Проблема в том, что я делаю паузу в своем коде сразу после вызова Arbiter. Активирую и проверяю свою базу данных, все выглядит нормально... если я избавлюсь от объявления паузы, пока не выполнится мой код, с базой данных ничего не случится, и никаких исключений брошены либо...
1 ответ
Проблема в том, что вы звоните Arbiter.Activate
в рамках ваших двух using
блоки. Не забывайте, что созданная вами задача CCR ставится в очередь, а текущий поток продолжается... сразу за рамками using
блоки. Вы создали условия гонки, потому что Choice
должен выполнить до connSP
а также cmdSP
расположены, и это произойдет только тогда, когда вы будете вмешиваться в синхронизацию потоков, как вы заметили при отладке.
Если вместо этого вам придется иметь дело с удалением вручную в обработчике делегатов для Choice
эта проблема больше не будет возникать, однако это делает для хрупкого кода, где легко пропустить утилизацию.
Я бы рекомендовал реализовать шаблон итератора CCR и собирать результаты с MulitpleItemReceive
так что вы можете сохранить свой using
заявления. Это делает для более чистого кода. С моей головы это будет выглядеть примерно так:
private IEnumerator<ITask> QueryIterator(
string command,
PortSet<Int32,Exception> resultPort)
{
using (SqlConnection connSP =
new SqlConnection(Settings.Default.nbfConn
+ ";MultipleActiveResultSets=true;Async=true"))
using (SqlCommand cmdSP = new SqlCommand())
{
Int32 result = 0;
connSP.Open();
cmdSP.Connection = connSP;
cmdSP.CommandTimeout = 150;
cmdSP.CommandText = "set arithabort on; " + commands[i];
yield return Arbiter.Choice(ApmToCcrAdapters.GetReader(cmdSP),
delegate(Int32 reader) { resultPort.Post(reader); },
delegate(Exception e) { resultPort.Post(e); });
}
}
и вы могли бы использовать что-то вроде этого:
var resultPort=new PortSet<Int32,Exception>();
foreach(var command in commands)
{
Arbiter.Activate(queue,
Arbiter.FromIteratorHandler(()=>QueryIterator(command,resultPort))
);
}
Arbiter.Activate(queue,
Arbiter.MultipleItemReceive(
resultPort,
commands.Count(),
(results,exceptions)=>{
//everything is done and you've got 2
//collections here, results and exceptions
//to process as you want
}
)
);