Tuesday, June 1, 2010

C#: DataRow Update error Index was out of range. Must be non-negative and less than the size of the collection.

You are threading and need to use Monitor.Enter when updating DataRows on a table.  Did you know that DataTables are not thread safe?  Even if your threads are updating different rows of the same DataTable, you can get concurrency errors that manifest themselves in weird ways.

Here’s the error I was getting:
Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index

Solution:
Use Monitor.Enter to protect updates to your DataRow.

        public void DoWorkUpdatingRow(object state)
        {
            List<DataRow> rowsToWorkOn = (List<DataRow>)state;
            foreach (DataRow dr in rowsToWorkOn)
            {
                Monitor.Enter(this);
                try
                {
                    dr["value"] = dr["id"] + " new value";
                }
                finally
                {
                    Monitor.Exit(this);
                }
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // Setup of data for worker threads
            DataTable dt = new DataTable();
            dt.Columns.Add("id", typeof(int));
            dt.Columns.Add("value", typeof(string));
            for (int i = 0; i < 5000; i++)
            {
                DataRow newRow = dt.NewRow();
                newRow["id"] = i;
                newRow["value"] = i + " text";
                dt.Rows.Add(newRow);
            }

            // Chunk out the work
            const int numberOfThreads = 4;
            int chunkSize = dt.Rows.Count / numberOfThreads;
            int currentRow = 0;

            for (int i = 0; i < numberOfThreads; i++)
            {
                List<DataRow> rowsToWorkOn = new List<DataRow>();

                int maxRowNumber = (currentRow + chunkSize);
                if (i == numberOfThreads)
                {
                    // Last chunk, don't run over the end of the datatable
                    maxRowNumber = dt.Rows.Count;
                }

                for (int j = currentRow; j < maxRowNumber; j++)
                {
                    rowsToWorkOn.Add(dt.Rows[j]);
                }
                currentRow += chunkSize;

                ThreadPool.QueueUserWorkItem(DoWorkUpdatingRow, rowsToWorkOn);
            }
        }

Saturday, May 29, 2010

C#: Chunking out work for worker threads and take advantage of multiple cores

The hardest part of adding threading to your application is deciding what to make your threads to.  You need to look for a unit of work that can be done independent of other processing.  In this case, we need to process every row of a DataTable and do some stuff.  I'll leave out the database calls, but I could be calling a stored procedure for every row and inserting records.  A lot of time would be wasted for each call to do it's round trip to the database, so a bunch of threads doing the calls and waiting will give us a huge burst of performance.


Say you have a DataTable, and you need to do some stuff with each row.  You can greatly improve the performance of your operation by creating worker threads.  It’s simple and easy to do!   Check out this simple example:

        private void button1_Click(object sender, EventArgs e)
        {
            // Setup of data for worker threads
            DataTable dt = new DataTable();
            dt.Columns.Add("id",typeof(int));
            dt.Columns.Add("value",typeof(string));
            for( int i = 0; i < 5000; i++ )
            {
                DataRow newRow = dt.NewRow();
                newRow["id"] = i;
                newRow["value"] = i + " text";
                dt.Rows.Add(newRow);
            }

            // Chunk out the work
            const int numberOfThreads = 4;
            int chunkSize = dt.Rows.Count / numberOfThreads;
            int currentRow = 0;
           
            for (int i = 0; i < numberOfThreads; i++)
            {
                List<DataRow> rowsToWorkOn = new List<DataRow>();

                int maxRowNumber = (currentRow + chunkSize);
                if (i == numberOfThreads)
                {
                    // Last chunk, don't run over the end of the datatable
                    maxRowNumber = dt.Rows.Count;
                }

                for (int j = currentRow; j < maxRowNumber; j++)
                {
                    rowsToWorkOn.Add(dt.Rows[j]);
                }
                currentRow += chunkSize;

                ThreadPool.QueueUserWorkItem(DoWork, rowsToWorkOn);
            }
        }

        public void DoWork(object state)
        {
            List<DataRow> rowsToWorkOn = (List<DataRow>)state;
            foreach (DataRow dr in rowsToWorkOn)
            {
                System.Diagnostics.Trace.WriteLine(dr["id"].ToString());
            }
        }

Friday, May 28, 2010

C#: Cross-thread operation not valid: Control ‘progressBar1’ accessed from a thread other than the thread it was created on

Error Message: Cross-thread operation not valid: Control ‘progressBar1’ accessed from a thread other than the thread it was created on.

This goes for any control that needs to be touched by a worker thread, you cannot do it directly.  You must do a callback so the main UI thread actually does the update.

Reproduce the problem:

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

            System.Threading.Thread worker = new System.Threading.Thread(doWork);
            worker.Start();
        }

        private void doWork()
        {
            for (int i = 0; i < progressBar1.Maximum; i++)
            {
                progressBar1.PerformStep();
            }
        }

Solution is:
Add this method, and instead of calling progressBar1.PerformStep(), call ActuallyPerformStep():
        delegate void CallPerformStep(ProgressBar myProgressBar);
        private void ActuallyPerformStep(ProgressBar myProgressBar)
        {
            if (myProgressBar.InvokeRequired)
            {
                CallPerformStep del = ActuallyPerformStep;
                myProgressBar.Invoke(del, new object[] { myProgressBar });
                return;
            }

            myProgressBar.PerformStep();
        }

And now the main UI thread is calling the UI Control.

Friday, January 8, 2010

C#: String.Format("Format", Arguments) for .Net

This is my personal reference for String.Format().

Here are the quick reference tables.  Below are the explanations.


Format

Int 1234

Decimal

1234.567

Decimal

0.1234567

Decimal
1234.5

0:c

$1,234.00

$1,234.57

$0.12

$1,234.50

0:d

1234

-

-

-

0:e

1.234000e
+003


1.234567e+003

1.234567e-001

1.234500e+003

0:f

1234.00

1234.57

0.12

1234.50

0:g

1234

1234.567

0.1234567

1234.5

0:n

1234.00

1,234.57

0.12

1,234.50

0:p

123,400.00
%


123,456.70
%


12.35 %

123,450.00
%


0:r

-

-

-

-

0:x

4d2

-

-

-

0:X

4D2

-

-

-

Padding/Precision:







0:d5

01234

-

-

-

0:e5

1.23400e+003

1.23457e+003

1.23457e-001

1.23450e+003

0:n5

1,234.00000

1,234.56700

0.12346

1,234.50000

0:c5

$1,234.00000

$1,234.56700

$0.12346

$1,234.50000

0:x5

004d2

-

-

-


Using: DateTime.Parse("December
13, 2008 5:01:30.7770 pm"
);



Format

DateTime (above date)

Jan
5 (rest same)


Description

0:D

Saturday, December
13, 2008


Wednesday, January
09, 2008


Long Date

0:F

Saturday, December
13, 2008 5:01:30 PM


Wednesday, January
09, 2008 5:01:30 PM


Full Date/Time
(long time)


0:G

12/13/2008 5:01:30
PM


1/9/2008 5:01:30 PM

General Date/Time
(long time)


0:M

December 13

January 09

Month Day

0:O

2008-12-13T17:01:30.7770000

2008-01-09T17:01:30.7770000

Round-Trip
Date/Time


0:R

Sat, 13 Dec 2008
17:01:30 GMT


Wed, 09 Jan 2008
17:01:30 GMT


RFC1123

0:T

5:01:30
PM


5:01:30
PM


Long
Time


0:U

Saturday, December
13, 2008 10:01:30 PM


Wednesday, January
09, 2008 10:01:30 PM


Universal Full
Date/Time (long time)


0:Y

December, 2008

January, 2008

Year Month

0:d

12/13/2008

1/9/2008

Short Date

0:f

Saturday,
December 13, 2008 5:01 PM


Wednesday,
January 09, 2008 5:01 PM


Full Date/Time
(short time)


0:g

12/13/2008 5:01 PM

1/9/2008 5:01 PM

General Date/Time (short time)

0:m

December
13


January
09


Month
Day


0:o

2008-12-13T17:01:30.7770000

2008-01-09T17:01:30.7770000

Round-Trip Date/Time

0:r

Sat, 13
Dec 2008 17:01:30 GMT


Wed, 09
Jan 2008 17:01:30 GMT


RFC1123

0:s

2008-12-13T17:01:30

2008-01-09T17:01:30

Sortable Date/Time

0:t

5:01 PM

5:01 PM

Short
Time


0:u

2008-12-13 17:01:30Z

2008-01-09 17:01:30Z

Universal Sortable
Date/Time


0:y

December,
2008


January,
2008


Year
Month




Useful Examples


Format

Use

Output

0:000-00-0000

SSN

012-34-5678

0:(###)###-####

US Phone Number

(555)555-5555

0:#-(###)###-####

US Phone
Number


1-(555)555-5555

0:1-(###)###-####

US Phone Number

1-(555)555-5555



Formatting DateTime objects with DateTime.ToString("Format Here"):


Format

Description

d

Non-padded
day


dd

Zero padded day

ddd

Short day
name (Tue)


dddd

Full day name (Tuesday)

h

Non-padded hour 12
hour format


hh

Zero padded hour 12
hour format


H

Non-padded hour 24
hour format


HH

Zero padded hour 24
hour format


m

Non-padded minute

mm

Zero padded minute

M

Non-padded Month

MM

Zero padded Month

MMM

Short month name
(Jun)


MMMM

Full month name
(June)


s

Non-padded seconds

ss

Zero-padded seconds

tt

AM/PM

yy

2 digit year

yyyy

4 digit year

:

As is

/

As is

MM/DD/yyyy

Sample: 06/14/2008

You can customize the above with Number Customizations:

# is a digit place holder.

0 is a zero place holder.

. is a printed decimal point.

, is a printed comma.

% means use the number as a percentage where 1 is 100%.

If you want to have the actual number be the percentage, prefix the % with a '.
Example:

String.Format("{0:##.00'%",
12.34); // 12.34%


To do left and right padding:

String.Format("{0,-9}",
1234); // “1234 


String.Format("{0,9}", 1234); // “     1234“


The Basics

How to call String.Format(): String.Format("{ArgumentPosition,Padding}",
arguments[] or argument,argument…);
 

String.Format("{0} {1}",
"Hello", "World");

Prints: Hello World

{0} and {1} are the positions of the arguments “Hello” and “World” specifying
where in the string they are top be used.

String.Format("First
Number: {0}, Third Number: {2}, Second Number: {1}."
, 1,2,3);

Prints:
First Number: 1, Third Number: 3, Second Number: 2.


You do not have to call ToString() on your arguments, String.Format()
is smart enough to do that for us. Suppose you created your own Class called
Currency, which overrides ToString(). When your instance of Currency has a value of $19.95,
you can use:

String.Format("MyCurrency value:
{0}"
, myCurrency);


Prints: MyCurrency value: $19.95

Continue that thought just a little further... if you print DataRow information:

String.Format("Column
'ID': {0}"
, dr["ID"]);