Dumbo
Dumbo

Reputation: 14112

Reporting progress of a LINQ query with background worker, how?

I am exprementing an application which prints out all the possible multiplies of integers in an array that equals size of the array:

private void bgw_DoWork(object sender, DoWorkEventArgs e)
{
    int progress = 0;

    int toCheck = int.Parse(textBox2.Text); //Number to check for
    int[] array = new int[toCheck];

    //Fill the array
    for (int i = 0; i < toCheck; i++) 
    {
        array[i] = i;
        bgw.ReportProgress(progress);
        progress += 1;
    }

    var result =
        from i1 in array
        from i2 in array
        where i1 * i2 == toCheck
        let square = i1 * i2
        select new { i1, i2, square }; //how to report progress here?

    foreach (var res in result)
    {
        textBox1.Text += res.i1 + " * " + res.i2 + " = " + res.square + Environment.NewLine;

        bgw.ReportProgress(progress);
        progress += 1;
    } 
}

The linq query itself is very time consuming specially if a big number should be checked for. Is there a way to report the progress of the linq query? Or I should leave linq and do this old-school mode?

Update This is my whole code. The progressbard does not fill after it got half-full. The first half is when the array is being created, the second part is when code tries to perform the linq query (That is why I think the reporting should be done in the linq query!) pardon my stupidity!!!

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

namespace BGWorkerTest
{
    public partial class Form1 : Form
    {
        private PerformanceCounter cpuCounter;
        private PerformanceCounter ramCounter;
        string[] diagnosticInfo = new string[2] { string.Empty, string.Empty };

        int toCheck = 0;

        StringBuilder sb;

        public Form1()
        {
            InitializeComponent();

            //Diagnostics
            cpuCounter = new PerformanceCounter();

            cpuCounter.CategoryName = "Processor";
            cpuCounter.CounterName = "% Processor Time";
            cpuCounter.InstanceName = "_Total";

            ramCounter = new PerformanceCounter("Memory", "Available KBytes");
        }


        private string[] GetDiagnostics()
        {
            diagnosticInfo[0] = string.Format("{0:0.##}", cpuCounter.NextValue()) + "%";
            diagnosticInfo[1] = ramCounter.NextValue() + "MB";

            return diagnosticInfo;
        }

        private void timerStStr_Tick(object sender, EventArgs e)
        {
            string[] temp = new string[2] { "", ""};
            temp = GetDiagnostics();
            ststrLabelCpu.Text = temp[0];
            ststrLabelMem.Text = temp[1];
        }

        private void button1_Click(object sender, EventArgs e)
        {
            ststrProgBar.Minimum = 0;
            ststrProgBar.Maximum = 0;

            toCheck = int.Parse(textBox2.Text);

            ststrProgBar.Minimum = 0;
            ststrProgBar.Maximum = toCheck * 2;
            ststrProgBar.Step = 1;

            //Starts the backgroundworker process asynchronously
            bgw.RunWorkerAsync();           
        }

        private void bgw_DoWork(object sender, DoWorkEventArgs e)
        {
            int progress = 0;

            int[] array = new int[toCheck + 1];

            for (int i = 0; i < toCheck + 1; i++)
            {
                array[i] = i;
                bgw.ReportProgress(progress);
                progress += 1;
            }

            var result =
                from i1 in array
                from i2 in array
                where i1 * i2 == toCheck
                let square = i1 * i2
                select new { i1, i2, square };

            sb = new StringBuilder();

            foreach (var res in result)
            {
               sb.AppendLine(res.i1 + " * " + res.i2 + " = " + res.square);

               bgw.ReportProgress(progress);
               progress += 1;

               Application.DoEvents();
            } 
        }

        private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            ststrProgBar.PerformStep();
        }

        private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                MessageBox.Show("Operation Cancelled");
            }
            else
            {
                MessageBox.Show("Operation Completed");

                textBox1.Text = sb.ToString();
            }
        }
    }
}

Upvotes: 1

Views: 2085

Answers (2)

Rawling
Rawling

Reputation: 50114

You could try this:

private static IEnumerable<T> ActAsEnumerated<T>(this IEnumerable<T> source, Action<T> act)
{
    foreach(var t in source)
    {
        act(t);
        yield return t;
    }
}

Usage could be something like:

var pairs = from i1 in array from i2 in array select new { int1 = i1, int2 = i2 };
var reportPairs = pairs.ActAsEnumerated(p => { bgw.ReportProgress(progress); progress += 1; });

var result =
    from pair in reportPairs
    where pair.int1 * pair.int2 == toCheck
    let square = pair.int1 * pair.int2
    select new { pair.int1, pair.int2, square };

which is really not very pretty. Really, I think you'd be better off doing this in a non-Linq fashion.

Upvotes: 2

TBohnen.jnr
TBohnen.jnr

Reputation: 5119

var result =      
    from int i1 in array      
    from int i2 in array      
    where i1 * i2 == toCheck      
    let square = getSquare(i1,i2)      
    select new { i1, i2, square };

And then create the method:

 public static int getSquare(int i, int i2)
    {
        Console.WriteLine("busy {0}", i);
        return i * i2;
    }

just change Console.WriteLine("busy {0}", i); to whatever you want to report

Upvotes: 1

Related Questions