如何在WPF中运行任务(我需要异步执行两个操作),当一个任务完成时,启动另一个任务

时间:2018-01-05 16:15:01

标签: c# wpf xaml asynchronous task

我从数据库导入了一些项目,通常会有大约20,000篇文章,所以我把它分成了一个新任务。

从MSSQL DB收到项目后,我希望与项目异步填充另一个列表,因为该列表后来被用作我的数据网格的源,这意味着我希望我的数据网格一直在更新,因为我想看到导入制品

这是我的代码:

// In my application I need to perform two task like Import a lot of articles from database, and fill global available list,
// that will be used later on some other window when windows open, reason of that is I want to have list ready because I'm 
// searching articles in memory list and I want them ready in my app

// This is an event that triggers everything, ImportArticles & Fill my global list with articles:

private void btnImportArticles_Click(object sender, RoutedEventArgs e)
{
     if (MessageBox.Show("Sure?", "Data sync", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
       return;
        Task.Factory.StartNew(() =>
        ImportDataFromServer()) // Import articles & groups from database
            .ContinueWith(task =>
            {
                // Start new task to update some list (BUT AFTER INSERT FROM MSSQL is done) which is Global available so call PrepareArticles() method which should Async store articles in case there is many of them.
                task.ContinueWith(task2 => {
                    PrepareArticles(); // But app freezes as hell
                }, System.Threading.CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());

            }, System.Threading.CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
    }
}

所以btnImportArticles_Click中的基本思想是在导入文章和组之后,让我们调用方法PrepareArticles,它应该用新导入的文章填充全局列表,所以我的代码继续:

private void ImportDataFromServer()
{
    using (SqlConnection connection = GetSqlConnection())
    {
        connection.Open();
        ImportGroups();
        ImportArticles();
    }
}

现在,我们可以发布ImportGroupsImportArticles方法的代码,这些方法可以访问数据库以获取文章':

private void ImportArticles()
{
    List<Article> newArticles = new List<Article>();
    using (SqlConnection connection = GetSqlConnection())
    {
        connection.Open();


        using (SqlCommand command = new SqlCommand())
        {
            command.CommandText = "SELECT COUNT(*) FROM [dbo].[Products] T1 INNER JOIN [dbo].[ProductGroup] T2 ON T1.Code = T2.Code";
            command.Connection = connection;
        }

        using (SqlCommand command = new SqlCommand())
        {
            command.CommandText = "SELECT T1.[Code], [Title], [Description],[Price] FROM [dbo].[Products] T1 INNER JOIN [dbo].[ProductGroup] T2 ON T1.Code = T2.Code";
            command.Connection = connection;

            using (SqlDataReader reader = command.ExecuteReader())
            {

                while (reader.Read())
                {
                  // Omitted for brevity
                }
            }
        }
    }
}



private void ImportGroups()
{
    using (SqlConnection connection = GetSqlConnection())
    {
        connection.Open();
        using (SqlCommand command = new SqlCommand())
        {
            command.CommandText = "SELECT [GroupId] FROM [dbo].[Groups]";
            command.Connection = connection;
            using (SqlDataReader reader = command.ExecuteReader())
            {
                if (reader.Read())
                {
                     // Omitted for brevity
                }
            }
        }
    }
    using (SqlConnection connection = GetSqlConnection())
    {
        connection.Open();
        using (SqlCommand command = new SqlCommand())
        {
            command.CommandText = "SELECT [GroupId], [Title] FROM [dbo].[GroupsArticles]";
            command.Connection = connection;
            SqlDataReader reader = command.ExecuteReader();
            while (reader.Read())
            {
                 // Omitted for brevity
            }
        }
    }
}

因为我不想错过这里的任何内容是PrepareArticles方法的定义,当我从数据库中获取文章时应该执行该方法,该方法正在填充全局可用的静态列表,因此代码在这里他们:

private void PrepareArticles()
{
    Task.Factory.StartNew(() =>
     Globals.GetArticlesReady())
         .ContinueWith(task3 =>
         {

         }, System.Threading.CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}

public static void GetArticlesReady()
{
    //Get All articles from database after they are imported.
    Articles = new List<Article>();
    Articles = ArticlesController..GetAll();
}  

谢谢你们! 干杯

1 个答案:

答案 0 :(得分:1)

免责声明:您可能希望了解有关async / await first here的更多信息,以便真正获得有关答案原因和方式的一些见解。

为了异步运行针对数据库的命令,已经存在基于任务的方法,例如ExecuteReaderAync。在这种情况下,您不必使用Task.Run()。关于这方面的一个很好的解读可以在the documentation

中找到

基于此,您可以重写代码以利用它:

private async void btnImportDataFromServer_Click(object sender, RoutedEventArgs e)
{
    if (MessageBox.Show("Sure?", "Data sync", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
        return;

    await ImportProducts();
    await LoadNewArticles(); // this will only start after ImportProducts() has finished succesfully        }
}

private async Task ImportProducts()
{
    List<Products> newProducts = new List<Products>();
    using (SqlConnection connection = GetSqlConnection())
    {
        await connection.OpenAsync();

        using (SqlCommand command = new SqlCommand())
        {
            command.CommandText = "SELECT COUNT(*) FROM [dbo].[Products] T1 INNER JOIN [dbo].[ProductGroup] T2 ON T1.Code = T2.Code";
            command.Connection = connection;

        }
        using (SqlCommand command = new SqlCommand())
        {
            command.CommandText = "SELECT T1.[Code], [Title], [Description],[Price] FROM [dbo].[Products] T1 INNER JOIN [dbo].[ProductGroup] T2 ON T1.Code = T2.Code";
            command.Connection = connection;
            using (SqlDataReader reader = await command.ExecuteReaderAsync())
            {
                while (await reader.ReadAsync())
                {
                    // Omitted for brevity
                }
            }
        }
    }
}

尽可能使用内置的async / await模式调用与数据库相关的方法。

现在,让我们谈谈LoadNewArticles方法。您没有显示完整的实现,但是您提到它将填充数据网格中的列表。您无法从非UI线程执行此操作,因此访问使用Task.RunTask.Factory.StartNew运行的代码中的任何控件都会给您带来麻烦。因此,要么使其同步,要么确保不要调用任何不安全的UI相关代码。请参阅the docs

(并且,如果您确实使用Control.Invoke从另一个线程安全地访问UI,请确实意识到此时没有异步工作。)