C# adding extra/unwanted parameters to stored procedure call

-1

I have a strange problem that is happening. On my development system everything runs fine but running the code on the production system it is adding extra parameters. Here is the code.

        public string Read(Setting SettingID, string SettingValue, int GameID)
        {
            SqlCommand sqlText;
            string SettingName = SettingsNames[(int)SettingID];

            using (SqlConnection cn = new SqlConnection(DataHandlers.ConnString))
            {
                sqlText = cn.CreateCommand();

                sqlText.CommandType = CommandType.StoredProcedure;
                sqlText.CommandText = "Proc_Settings";
                sqlText.Parameters.Clear();
                sqlText.Parameters.Add(new SqlParameter("SettingName", SettingName));
                sqlText.Parameters.Add(new SqlParameter("SettingValue", SettingValue));
                sqlText.Parameters.Add(new SqlParameter("Dir", Direction.Read));
                sqlText.Parameters.Add(new SqlParameter("GameID", GameID));
                sqlText.Parameters.Add("@Return", SqlDbType.Int).Direction = ParameterDirection.ReturnValue;

![Text](E://pulltab/capture.png)

The first 2 parameters add fine. On the third parameter is adds 'DIR' and @Return but @Return is an input. the same happens for the 4th parameter and then it adds the 5th as a return. The function is being called from another thread when the issue happens. The thread is a com port handler. If I disable the com ports in bios it works fine, this makes sense since my dev computer does not have the same number of ports so the one for the device is missing. Once I enable the ports and the routine starts up the polling thread is when the issue begins. I have looked at the different threads in VS and the only one that takes me to code related to the issue is the current one I am debugging. Am I causing a threading violation here?

this is the poll routine

        private void Poll()
        {
            Actions.init(buffer, length, ComDll, Port);             //Function to Initialize

            while (Active)
            {
                // Process the com port
                // Currently doesn't get here
            }
         }

And here is the init function,

        public static void init(byte[] buffer, int length, ID003CommandCreater ComDll, SerialPort Port)
        {
            byte enable1 = 0;
            byte enable2 = 0;
            byte[] status = new byte[255]; //Buffer to read data from the serial port
            bool initializing = true;

            while (initializing)
            {
                try
                {
                    // Initializes the device
                    ComDll.Reset(buffer);
                    length = (int)buffer[1];
                    Port.Write(buffer, 0, length);
                    System.Threading.Thread.Sleep(3000);
                    Port.Read(status, 0, 255);               //Fails when device not connected

                    … More init commands sent

                    initializing = false;
                }
                catch
                {
                    if (Port.PortName != settings.Read(Settings.Setting.ComPort, Port.PortName, 0))
                    {
                        Port.Close();
                        initializing = false;
                    }
                    //JCM.BVstatus = "Com Failure";
                    //JCM.bvComFailure = true;
                }
            }
        }

Here is the error.

"System.Data.SqlClient.SqlException (0x80131904): Procedure or function Proc_Settings has too many arguments specified.\nProcedure or function Proc_Settings has too many arguments specified.\r\n at System.Data.SqlClient.SqlConnection.OnError (System.Data.SqlClient.SqlException exception, System.Boolean breakConnection, System.Action1[T] wrapCloseInAction) [0x0004c] in <290425a50ff84a639f8c060e2d4530f6>:0 \r\n at (wrapper remoting-invoke-with-check) System.Data.SqlClient.SqlConnection.OnError(System.Data.SqlClient.SqlException,bool,System.Action1<System.Action>)\r\n at System.Data.SqlClient.SqlInternalConnection.OnError (System.Data.SqlClient.SqlException exception, System.Boolean breakConnection, System.Action1[T] wrapCloseInAction) [0x00013] in <290425a50ff84a639f8c060e2d4530f6>:0 \r\n at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning (System.Data.SqlClient.TdsParserStateObject stateObj, System.Boolean callerHasConnectionLock, System.Boolean asyncClose) [0x00152] in <290425a50ff84a639f8c060e2d4530f6>:0 \r\n at System.Data.SqlClient.TdsParser.TryRun (System.Data.SqlClient.RunBehavior runBehavior, System.Data.SqlClient.SqlCommand cmdHandler, System.Data.SqlClient.SqlDataReader dataStream, System.Data.SqlClient.BulkCopySimpleResultSet bulkCopyHandler, System.Data.SqlClient.TdsParserStateObject stateObj, System.Boolean& dataReady) [0x009b9] in <290425a50ff84a639f8c060e2d4530f6>:0 \r\n at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData () [0x00040] in <290425a50ff84a639f8c060e2d4530f6>:0 \r\n at System.Data.SqlClient.SqlDataReader.get_MetaData () [0x00031] in <290425a50ff84a639f8c060e2d4530f6>:0 \r\n at (wrapper remoting-invoke-with-check) System.Data.SqlClient.SqlDataReader.get_MetaData()\r\n at System.Data.SqlClient.SqlCommand.FinishExecuteReader (System.Data.SqlClient.SqlDataReader ds, System.Data.SqlClient.RunBehavior runBehavior, System.String resetOptionsString) [0x000d3] in <290425a50ff84a639f8c060e2d4530f6>:0 \r\n at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds (System.Data.CommandBehavior cmdBehavior, System.Data.SqlClient.RunBehavior runBehavior, System.Boolean returnStream, System.Boolean async, System.Int32 timeout, System.Threading.Tasks.Task& task, System.Boolean asyncWrite, System.Data.SqlClient.SqlDataReader ds) [0x004d8] in <290425a50ff84a639f8c060e2d4530f6>:0 \r\n at System.Data.SqlClient.SqlCommand.RunExecuteReader (System.Data.CommandBehavior cmdBehavior, System.Data.SqlClient.RunBehavior runBehavior, System.Boolean returnStream, System.Threading.Tasks.TaskCompletionSource1[TResult] completion, System.Int32 timeout, System.Threading.Tasks.Task& task, System.Boolean asyncWrite, System.String method) [0x00079] in <290425a50ff84a639f8c060e2d4530f6>:0 \r\n at System.Data.SqlClient.SqlCommand.RunExecuteReader (System.Data.CommandBehavior cmdBehavior, System.Data.SqlClient.RunBehavior runBehavior, System.Boolean returnStream, System.String method) [0x0000b] in <290425a50ff84a639f8c060e2d4530f6>:0 \r\n at System.Data.SqlClient.SqlCommand.ExecuteReader (System.Data.CommandBehavior behavior) [0x0003c] in <290425a50ff84a639f8c060e2d4530f6>:0 \r\n at System.Data.SqlClient.SqlCommand.ExecuteReader () [0x0000e] in <290425a50ff84a639f8c060e2d4530f6>:0 \r\n at (wrapper remoting-invoke-with-check) System.Data.SqlClient.SqlCommand.ExecuteReader()\r\n at Settings.Read (Settings+Setting SettingID, System.String SettingValue, System.Int32 GameID) [0x000cb] in E:\Unity\xxxx\Assets\xxxx\Scripts\Database\Settings.cs:192 \r\nClientConnectionId:42a5adfe-d934-496e-b678-456fde7d2622\r\nError Number:8144,State:2,Class:16"

I have used a little threading before but might be misunderstanding some of this. In this case the Init must complete successfully before the poll thread continues. Once the init is done then the thread will enter a loop to handle the com port. If I do not wait on the hardware then the loop will try to send more data from the com port which will mess up the configuration. There are about 6 steps that I must do in a sequence to successfully initialize the hardware. That first command resets the device and it takes just short of 3 seconds for it to initialize before I can continue talking to it. Do i want the thread to continue? This thread only runs the code for the device, nothing else. All of the other code continues. The Read routine is called from multiple threads. It is used throughout the program.

c#
sql
unity3d
asked on Stack Overflow Sep 11, 2020 by bgs801 • edited Sep 16, 2020 by bgs801

1 Answer

0

When you use System.Threading.Thread.Sleep(3000); you block the current thread as a means to wait for the task to finish and that can result in deadlocks and blocked context threads

When you wait for a certain amount of time you should rather wait with await System.Threading.Tasks.Task.Delay(3000);. Because this command doesn't block the current thread.

To use this command you need to make your method asynchronous.

Your Code made asynchronous:

public static async System.Threading.Tasks.Task InitAsync(
    byte[] buffer,
    int length,
    ID003CommandCreater ComDll,
    SerialPort Port) 
{
    byte enable1 = 0;
    byte enable2 = 0;
    byte[] status = new byte[255]; //Buffer to read data from the serial port
    bool initializing = true;

    while (initializing) {
        try {
            // Initializes the device
            ComDll.Reset(buffer);
            length = (int)buffer[1];
            Port.Write(buffer, 0, length);

            // Doesn't block thread anymore, because we wait until the task is finished.
            await System.Threading.Tasks.Task.Delay(300);
            Port.Read(status, 0, 255);

            … More init commands sent

            initializing = false;
        }
        catch {
            if (Port.PortName != settings.Read(Settings.Setting.ComPort, Port.PortName, 0)) {
                Port.Close();
                initializing = false;
            }
            //JCM.BVstatus = "Com Failure";
            //JCM.bvComFailure = true;
        }
    }
}

I would advise you to make methods that call this method asynchronous as well but if that is not possible, then you need to ensure that there are no deadlocks and that is possible with a small workaround.

To call a asynchronous method in a synchronous method we first need to add a class that clears the thread so we don't block it

NoSynchronizationContextScope Class:

public static class NoSynchronizationContextScope {
    public static Disposable Enter() {
        var context = SynchronizationContext.Current;
        SynchronizationContext.SetSynchronizationContext(null);
        return new Disposable(context);
    }

    public struct Disposable : IDisposable {
        private readonly SynchronizationContext _synchronizationContext;

        public Disposable(SynchronizationContext synchronizationContext) {
            _synchronizationContext = synchronizationContext;
        }

        public void Dispose() =>
            SynchronizationContext.SetSynchronizationContext(_synchronizationContext);
    }
}

Now we can call asynchronous method anywhere in our code if we use these lines of code.

Calling Asynchronous method in a synchronous method:

private void MySynchronousMethodLikeDisposeForExample() {
    using (NoSynchronizationContextScope.Enter()) {
        InitAsync().Wait();
    }
}
answered on Stack Overflow Sep 12, 2020 by MathewHD

User contributions licensed under CC BY-SA 3.0