-1

I have a demo application from the supplier. This demo application allows me to connect to a RFID reader via a network connection. Once the connection is established I can then scan for RFID tags and the tag data is then displayed on the application.

The eventhandler is raised whenever the CAlienServer receives a message from the RFID reader.

private void addServerEvents(int idx)
{
    mServers[idx].ServerMessageReceived += new CAlienServer.ServerMessageReceivedEventHandler(mServer_ServerMessageReceived);
    mServers[idx].ServerConnectionEstablished += new CAlienServer.ServerConnectionEstablishedEventHandler(mServer_ServerConnectionEstablished);
    mServers[idx].ServerConnectionEnded += new CAlienServer.ServerConnectionEndedEventHandler(mServer_ServerConnectionEnded);
    mServers[idx].ServerSocketError += new CAlienServer.ServerSocketErrorEventHandler(mServer_ServerSocketError);
}

private void mServer_ServerMessageReceived(string msg)
{
    if (msg != null)
    {
        displayText(msg.Insert(msg.IndexOf(" ") + 1, "\r\n"));
    }    
}

private void displayText(String data)
{
    try
    {
        if (this.InvokeRequired)
        {
            object[] temp = {data};
            this.Invoke(new displayMessageDlgt(displayText),temp);
            return;
        }
        else
        {
            txtNotifications.Text += data.Replace("\0","") + "\r\n";
            Task task = insert(data);
            NotifyInfo ni = null;
            AlienUtils.ParseNotification(data, out ni);
            if (ni != null)
            {
                lblReaderName.Text = ni.ReaderName;
            }
        }
    }
    catch(Exception ex)
    {
        Debug.WriteLine("Exception in the DiscplayText(): " + ex.Message);
    }
}

I want to know run another function that stores the data in the variable 'data' or 'msg' into a database. I know calling a synchronous function will not work because what if the programming is running the insertion of the data into the database function and the reader picks up new tags. Hence a synchronous function will not work. I am thinking of using an asynchronous function instead. So my database insertion function is. Is this the correct way to go about it?

private async Task insert(string rfid)
{
   await Task.Factory.StartNew(()=>
   {
      using (var connection = new MySqlConnection("server=localhost;user id='" + username_mysql + "';database=new;password='" + password_mysql + "'"))
      {
        connection.Open();

        try
        {
           string sql = "INSERT INTO `data` (`RFID TAG`) VALUE (@rfid)";

           MySqlCommand cmd = new MySqlCommand(sql, connection);

           cmd.Parameters.Add("@rfid", MySqlDbType.VarChar).Value = rfid;

           if (cmd.ExecuteNonQuery() == 1)
           {
             

           }
        }
        catch (Exception ex)
        {
            
        }
    }
   });
}

Error 1: enter image description here

Update:

Code:

    private void insert(string rfid)
    {
        using (var connection = new MySqlConnection("server=localhost;user id='" + username_mysql + "';database=alien;password='" + password_mysql + "'"))
        {
            connection.Open();

            try
            {
                string sql = "INSERT INTO `data` (`DATA`) VALUE (@data)";

                MySqlCommand cmd = new MySqlCommand(sql, connection);

                cmd.Parameters.Add("@data", MySqlDbType.VarChar).Value = rfid;

                if (cmd.ExecuteNonQuery() == 1)
                {
                    Debug.WriteLine("Successfull: " + rfid);
                    textBox2.BackColor = Color.Green;

                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine("Exception in the insert(): " + ex.Message);
            }
        }
    }

private void displayText(String data)
{
    try
    {
            if (this.InvokeRequired)
            {
                object[] temp = {data};
                this.Invoke(new displayMessageDlgt(displayText), temp);
                return;
            }
            else
            {
                txtNotifications.Text += data.Replace("\0","") + "\r\n";
                //await Task.Run(() => insert(data));
                insert(data);
                textBox2.BackColor = Color.Red;

                NotifyInfo ni = null;
                AlienUtils.ParseNotification(data, out ni);
                if (ni != null)
                {
                    lblReaderName.Text = ni.ReaderName;
                }
            }
        }
        catch(Exception ex)
        {
            Debug.WriteLine("Exception in the DisplayText(): " + ex.Message);
        }
  }

SERVER MESSAGE is from the mServer_ServerMessageReceived(string msg) method Succesfull is from the insert() method.

This clearly shows that all of that tag data is been inserted in the database.

Debug output:

SERVER MESSAGE: 00:1B:5F:00:F9:B4 0004,3,1

SERVER MESSAGE: 00:1B:5F:00:F9:B4 A123 0001,3,1

Successfull: 00:1B:5F:00:F9:B4 0004,3,1

SERVER MESSAGE: 00:1B:5F:00:F9:B4 A641 2541,3,1

Successfull: 00:1B:5F:00:F9:B4 A123 0001,3,1

SERVER MESSAGE: 00:1B:5F:00:F9:B4 0014,3,1

Successfull: 00:1B:5F:00:F9:B4 A641 2541,3,1

SERVER MESSAGE: 00:1B:5F:00:F9:B4 5253 7AB2 0200 0000 0000 1002,3,1

Successfull: 00:1B:5F:00:F9:B4 0014,3,1

SERVER MESSAGE: 00:1B:5F:00:F9:B4 A064 0001 0601,3,1

Successfull: 00:1B:5F:00:F9:B4 5253 7AB2 0200 0000 0000 1002,3,1

SERVER MESSAGE: 00:1B:5F:00:F9:B4 0000 0000,3,1

SERVER MESSAGE: 00:1B:5F:00:F9:B4 0001,3,1

SERVER MESSAGE: 00:1B:5F:00:F9:B4 2123 6521,3,1

SERVER MESSAGE: 00:1B:5F:00:F9:B4 0016,3,1

Successfull: 00:1B:5F:00:F9:B4 A064 0001 0601,3,1

Successfull: 00:1B:5F:00:F9:B4 0000 0000,3,1

Successfull: 00:1B:5F:00:F9:B4 0001,3,1

Successfull: 00:1B:5F:00:F9:B4 2123 6521,3,1

Successfull: 00:1B:5F:00:F9:B4 0016,3,1

Paul
  • 21
  • 5
  • 1
    No, it isn't the right way of using async, for multiple reasons, including the [use of `StartNew`](https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html), the [fire and forget](https://stackoverflow.com/q/46053175/11683), and the very reasoning of "synchronous function will not work". – GSerg Mar 15 '21 at 14:59
  • *I know calling a synchronous function will not work because what if the programming is running the insertion of the data into the database function and the reader picks up new tags.* Sounds like you have a concern about concurrency. That has nothing to do with `async`. – John Wu Mar 15 '21 at 15:02
  • @GSerg I did not update my .NET version so the Run option was not available. I have updated my .NET now and I can use Task.Run(....). What can I use besides asynch? – Paul Mar 15 '21 at 15:09
  • @JohnWu can you be more specific on what I should then do?.... – Paul Mar 15 '21 at 15:10
  • 1
    Remove `Task.Factory.StartNew`. Change `cmd.ExecuteNonQuery` to `await cmd.ExecuteNonQueryAsync`. If you have an actual concurrency problem, post a different question and include details. – John Wu Mar 15 '21 at 15:12
  • 2
    As a side note, according to the [official guidelines](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/capitalization-conventions), method names in C# follow the PascalCase pattern. Also asynchronous methods should have the [`Async`](https://learn.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap#naming-parameters-and-return-types) suffix. So it's `InsertAsync`, not `insert`. – Theodor Zoulias Mar 15 '21 at 15:22
  • @JohnWu how would I know if I have concurrency problems? – Paul Mar 15 '21 at 15:59
  • 1
    @Paul Implement your code and test to see if you can reproduce an issue of the kind that you mentioned ("running the insertion of the data into the database function and the reader picks up new tags") – John Wu Mar 15 '21 at 17:31

1 Answers1

1

You should probably use ExecuteNonQueryAsync instead. Note that this may or may not actually exectue asynchronous.

For Oracles MySql connector all async method execute synchronously. MySqlConnector claims to provide true async support, but I have not tested this. ( Thanks to Bradley Grainger for additional information.)

Is you determine that the query is actually running synchronous it might be reasonable to run the query on a background thread to avoid blocking the UI (if it is a UI application). If so, it would probably be best to first make a synchronous method.

private void insert(string rfid){
    ...
}

And then call it like

private async Task displayText(String data){
    ....
    await Task.Run(() => insert(rfid));
    ...
}

If you do this you need to guarantee that the method is thread safe. If you cannot guarantee this, you should not run it on another thread. Note that you should ensure exceptions are handled correctly. I.e.

  • Avoid en empty catch, all exceptions should be handled somehow. If you could not insert the required data the user should probably be informed.
  • All tasks should be awaited, otherwise any exceptions will be lost.
  • Avoid using async void since this makes it impossible for the caller await, and therefore cannot receive any exceptions.
  • For event handlers (i.e. pressing a button) you will need to use async void, in this case you should ensure that any exceptions are caught and take appropriate action. Probably informing the user of the failure, and/or terminating the application.
JonasH
  • 28,608
  • 2
  • 10
  • 23
  • what is the difference between making displayText() an asynch method rather than insert() – Paul Mar 15 '21 at 16:35
  • 2
    Note that with MySql.Data, all "Async" methods actually execute synchronously: https://bugs.mysql.com/bug.php?id=70111 The only way to get true async I/O with MySQL in .NET is to switch to a different ADO.NET library: https://mysqlconnector.net/tutorials/migrating-from-connector-net/ – Bradley Grainger Mar 15 '21 at 22:28
  • 1
    @Paul, whenever you call an async method you need to handle the result, even if this is just an execption, this is most easily done by awaiting. Therefore the caller also need to be `async Task`. This continues until you reach a point where you can handle the exception appropriately, probably in the UI layer. – JonasH Mar 16 '21 at 08:04
  • 1
    @Bradley Grainger thank you for the information, added it to the answer. I suspected as much, but did not have the references. – JonasH Mar 16 '21 at 08:05
  • @JonasH I am getting at error (see edit in my post), error code CS0407: 'Task....' has wrong return type – Paul Mar 16 '21 at 10:46
  • @Paul that is because the signature does not match. It might be reasonable to use `async void` in that case. If you do so, ensure you are handling all the exceptions in the DisplayText method. – JonasH Mar 16 '21 at 10:52
  • @JonasH are there any documentation that explains how to handle all the expectations? – Paul Mar 16 '21 at 11:33
  • 1
    @Paul there are lots of guides and best practices that describes how you should handle exceptions. [Microsofts programming guide about exceptions](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/exceptions/) might be a good start. – JonasH Mar 16 '21 at 11:42
  • @JonasH "https://pdfs.semanticscholar.org/dcb5/4294e14ec573e497b8730eba940c2104db1e.pdf" I see from this article they also using the same RFID reader API and are also sending queries to a database but they are not using async. So I will test my code with async and see if I get any issues. – Paul Mar 16 '21 at 12:40
  • @BradleyGrainger getting an error installing your connector "Severity Code Description Project File Line Suppression State Error Some NuGet packages were installed using a target framework different from the current target framework and may need to be reinstalled. Visit https://docs.nuget.org/docs/workflows/reinstalling-packages for more information. Packages affected: MySqlConnector, System.Buffers, System.Memory, System.Numerics.Vectors, System.Runtime.CompilerServices.Unsafe, System.Threading.Tasks.Extensions Ex9 - Notifications and Streaming on Network 0" – Paul Mar 16 '21 at 13:30
  • @BradleyGrainger I have .NET4.5 and 4.7.2 – Paul Mar 16 '21 at 13:35
  • @JonasH something very strange is happening... So I implemented with any asynch and awaits. However the code is behaving in an asynchronous manner. I know this because of the debug output in the update. So there a need to use asynch and await? – Paul Mar 16 '21 at 16:01
  • @JonasH Because if it was truely sychronous then immediately after the SERVER MESSAGE I should see the Successful below that. But I am seeing several SERVER MESSAGE and then the Successful. And all the data is captured because the SERVER MESSAGE count is equal to the Successful count – Paul Mar 16 '21 at 16:05