-1

My requirement is, need to render all framework elements which are bound with data using async and await.

Tried below possibilities:

  1. If i am using asyncmethod().result it will block the UI and it will be waiting for a long time to completion.

  2. If I am using await keyword the controlstoconfigure foreach(2nd one) will be hit for before completion of parallel.foreach()

So can you please suggest me for rendering elements, call the 2nd for each after completion of parallel foreach or without blocking UI?

Code snippet:

1)Renderingwidget - this method is used to get framework elements in the collection which is bound with data.

2)FetchData - this method is used to get the data from the server for particular framework element.

3)GetTablefromserver - this method is used to get the data based on queries.

    public async void RenderingWidget()
    {
        ConcurrentDictionary<string, EngineChangedEventArgs> controlsToConfigure = new ConcurrentDictionary<string, EngineChangedEventArgs>();
        foreach (var engines in MainWindow.ViewModel.RelationalDataManagerList.Values)
        {
            Dictionary<string, FrameworkElement> controlcollection = CurrentDashboardReport.WidgetCollection;
            Parallel.ForEach(controlcollection, async item =>
             {
                 try
                 {
                     try
                     {
                         controlsToConfigure.TryAdd(item.Key, await FetchDataInParallel(MainWindow.ViewModel.RelationalDashboardReportList[engines.DataSourceName].Reports, item.Key,));
                     }
                     catch (Exception ex)
                     {
                         ExceptionLog.WriteExceptionLog(ex, null);
                         throw new ParallelException(ex, item.Key);
                     }
                 }
                 catch (ParallelException ex)
                 {
                     exceptions.Enqueue(ex);
                     ExceptionLog.WriteExceptionLog(ex, GeneratedQueryText);
                 }
             });
            if (exceptions.Count > 0)
            {
                foreach (var nonRenderedControls in exceptions)
                {
                    controlsToConfigure.TryAdd(nonRenderedControls.ReportName, await FetchDataInParallel(CurrentDashboardReport.Reports, nonRenderedControls.ReportName));
                }
            }
        }
        foreach (var control in controlsToConfigure)
        {
            (CurrentDashboardReport.WidgetCollection[control.Key] as DashboardDataControl).CurrentDashboardReport.OnActiveColumnsChanged(control.Value);                   
        }
    }

    public async Task<EngineChangedEventArgs> FetchDataInParallel(RelationalReportCollection reports, string myReport)
    {
        var dataTable = await GetTableFromServer(reports.Query);
        eventArgs = new EngineChangedEventArgs
        {
            ItemsSource = dataTable
        };
        return eventArgs;
    }

    public async Task<DataTable> GetTableFromServer(string query)
    {
        var resultTable = new DataTable
        {
            Locale = CultureInfo.InvariantCulture
        };
        SqlConnection sqlConnection = new SqlConnection(connectionString);
        SqlCommand command = new SqlCommand(query, sqlConnection)
        SqlDataReader dataReader = null;
        try
        {
            if (sqlConnection.State != ConnectionState.Open)
            {
                sqlConnection.Open();
            }
            dataReader =await command.ExecuteReaderAsync();
            resultTable.Load(dataReader);
            return resultTable;
        }
        finally
        {
            if (dataReader != null)
            {
                dataReader.Dispose();
            }
            command.Dispose();
            return resultTable;
        }
    }
Pandi
  • 471
  • 3
  • 17

1 Answers1

2

Parallel.ForEach doesn't play well with async/await as you release the thread for the duration of the async call. Please refer to the following question for more information about this: Nesting await in Parallel.ForEach

You may just call Parallel.ForEach on a thread pool thread and then use the Task.WhenAll method to wait until all tasks have completed Before you continue:

public async void RenderingWidget()
{
    ConcurrentDictionary<string, EngineChangedEventArgs> controlsToConfigure = new ConcurrentDictionary<string, EngineChangedEventArgs>();

    List<Task> tasks = new List<Task>();

    foreach (var engines in MainWindow.ViewModel.RelationalDataManagerList.Values)
    {
        Dictionary<string, FrameworkElement> controlcollection = CurrentDashboardReport.WidgetCollection;
        tasks.Add(Task.Run(() =>
        {
            Parallel.ForEach(controlcollection, item =>
            {
                try
                {
                    try
                    {
                        controlsToConfigure.TryAdd(item.Key, FetchDataInParallel(MainWindow.ViewModel.RelationalDashboardReportList[engines.DataSourceName].Reports, item.Key).Result);
                    }
                    catch (Exception ex)
                    {
                        ExceptionLog.WriteExceptionLog(ex, null);
                        throw new ParallelException(ex, item.Key);
                    }
                }
                catch (ParallelException ex)
                {
                    exceptions.Enqueue(ex);
                    ExceptionLog.WriteExceptionLog(ex, GeneratedQueryText);
                }
            });
            if (exceptions.Count > 0)
            {
                foreach (var nonRenderedControls in exceptions)
                {
                    controlsToConfigure.TryAdd(nonRenderedControls.ReportName, FetchDataInParallel(CurrentDashboardReport.Reports, nonRenderedControls.ReportName).Result);
                }
            }
        }));
    }

    await Task.WhenAll(tasks);

    foreach (var control in controlsToConfigure)
    {
        (CurrentDashboardReport.WidgetCollection[control.Key] as DashboardDataControl).CurrentDashboardReport.OnActiveColumnsChanged(control.Value);
    }
}

public async Task<EngineChangedEventArgs> FetchDataInParallel(RelationalReportCollection reports, string myReport)
{
    var dataTable = await GetTableFromServer(reports.Query).ConfigureAwait(false);
    return new EngineChangedEventArgs
    {
        ItemsSource = dataTable
    };
}
mm8
  • 163,881
  • 10
  • 57
  • 88
  • In My code FetchData method is declared as Async method and which is returned a task.This method is used in various places.May i know how to resolve this? If i remove the async it will be act as synchronous – Pandi Dec 11 '17 at 10:30
  • Don't use Parallel.ForEach or block, for example using the Result property. This should be safe if you call ConfigureAwait(false) in the FetchDataInParallel (which you should do in "service" methods). – mm8 Dec 11 '17 at 10:33
  • Please remember to accept the answer if your issue has been solved. – mm8 Dec 12 '17 at 10:48
  • While run source If i use .Result in fetchdata method, it will not go to next line. It will wait for long time to completion. – Pandi Dec 13 '17 at 04:43
  • Where does it block? – mm8 Dec 13 '17 at 10:59