Microsoft Robotics и Sql - PullRequest
       19

Microsoft Robotics и Sql

2 голосов
/ 05 ноября 2010

У меня проблема с реализацией 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 Ответ

3 голосов
/ 01 декабря 2010

Проблема здесь в том, что вы звоните 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
        }
    )
);
...