0

I'm trying to manipulate data on a webpage and I have an API that can do that, but I ran into some problems.

There is a part in my code, where I insert-, manipulate and then update data to the webpage. The API uses asynchronous methods to insert and update data to the webpage, so I can't change that.

Here is the code, where the problem occurs:

namespace Web
{
    class WebDatabase
    {
        WebApi api;

        internal static async Task InsertCategory(string category)
        {
            await api.Insert(category);  //  Insert data to the database
            //Some manipulation on the category
            //..
            //..
            await api.GetCategoryIdFromDatabase(category);  //  Getting id of data from the database
            await api.Update(category);   //  Updating that data based on id in the database
        }
    }
}

As you can see, after I insert the data into the database I have to get the automatically generated ID from the database in order to be able to update the data that I manipulated, but sometimes it inserts it before trying to get the ID and sometimes not! How can I fix it? I must be doing something wrong, but I think I kinda tried everything to make it work, but still no success :(

Here is the rest of the code, maybe it will help you to better see what's going on.


Main

namespace Main
{
    class Program
    {
        Thread thread;

        private void buttonClick(object sender, EventArgs e)
        {
            if (thread != null && thread.IsAlive)
            {
                Console.WriteLine("The process is still going!");
                return;
            }

            thread = new Thread(Send);
            thread.Start();
        }

        private async void Send()
        {
            await WebDatabase.SendCategoryList(category);
        }
    }
}

WebDatabase

namespace Web
{
    class WebDatabase
    {
        WebApi api;

        internal static async Task SendCategoryList(string category)
        {
            await api.Insert(category);   //   Inserting data to the database
            //Some manipulation on the category
            //..
            //..
            //   
            await api.GetCategoryIdFromDatabase(category);    //  Trying to get ID from database, but the data is not there yet, so it throws an error
            await api.Update(category);   //  Now I can't update my poor category in the database :(
        }
    }
}

API

namespace API
{
    class WebApi
    {
        WebAPI webapi = new WebAPI();

        public async Task Insert(List<string> categoriesList)
        {
            for (int i = 0; i < categoriesList.Count; i++)
            {
                await Update(categoriesList[i]);
            }
        }

        public async Task Update(string categories)
        {
            await webapi.Category.Add(categories);
        }

        public async Task GetCategoryIdFromDatabase(string categories)
        {
            await webapi.Category.GetAll().Result.FirstOrDefault(productCategory => productCategory.name.Trim().ToLower() == category.name.Trim().ToLower()).id.Value;
        }
    }
}

Any help would be greatly appreciated!

  • 3
    Take a look at this: [Is it ok to use “async” with a ThreadStart method?](https://stackoverflow.com/questions/44364092/is-it-ok-to-use-async-with-a-threadstart-method) In short the `Thread` constructor is not async-friendly. The method passed (`Send`) is `async void`, which is [something to avoid](https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming#avoid-async-void). – Theodor Zoulias Jan 20 '21 at 10:29
  • @TheodorZoulias Thanks for the tip! I changed the line ```thread = new Thread(Send);thread.Start();``` to ```await Task.Run(Send);``` but now I get and exception at the update method saying something about my API connection not being valid... Maybe it has to do something with the way I'm getting my ID from the database? I'll update the question to show how it works for me – Martin Serdült Jan 20 '21 at 10:47
  • @TheodorZoulias I updated the GetCategoryIdFromDatabase part! – Martin Serdült Jan 20 '21 at 10:55
  • @MartinSerdült there's no point in using raw threads when working with tasks. `async void` is only meant for event handlers. You should use `async Task Main()`, *not* use a thread at all, change `Send` to `async Task Send` and call it from `Main` with `await Send()` – Panagiotis Kanavos Jan 20 '21 at 10:57
  • 1
    As for `GetCategoryIdFromDatabase` is looks like you used the "generic" repository *antipattern*, loaded the entire table in memory while blocking with `webapi.Category.GetAll().Result` then tried to filter the in-memory results. DbContext is a higher-level abstraction than a repository. You could load only what you want in asynchronously with `var category=await dbContext.Categories.Where(cat=>cat.name==category.name).Select(cat=>cat.Id).FirstOrDefaultAsync();` – Panagiotis Kanavos Jan 20 '21 at 11:00
  • More bugs, caused because you loaded the entire table `productCategory.name.Trim().ToLower() == category.name.Trim().ToLower()` can't use any indexes, because it searches for the *result* of applying a function to a field. Indexes are built using field values, so they can't be used when you want to filter by a derived value. If you want case-insensitive searches, make sure your database uses a case-insensitive collation. That's the default used when creating new databases in most cases. – Panagiotis Kanavos Jan 20 '21 at 11:04
  • The `.Result` in the `await webapi.Category.GetAll().Result...` line is also highly suspect. Presumably it's the `Task.Result` property, which is a blocking call, much like the `Wait` method. Using `await` instead is much preferable. This may also help in breaking this desperately long line into two smaller pieces, so that we, and yourself, have a better idea of what's going on there. – Theodor Zoulias Jan 20 '21 at 11:09

1 Answers1

0

Never use async void for any of the method. Instead use async Task<bool> or async Task<int>

When you use async void the exceptions that are thrown might be caught properly.