Home > Blockchain >  DataRow.SetField() gives a null ref exception when adding data to a column I previously deleted then
DataRow.SetField() gives a null ref exception when adding data to a column I previously deleted then

Time:07-01

UPDATE
I think I have found what is causing the issue here https://stackoverflow.com/a/5665600/19393524
I believe my issue lies with my use of .DefaultView. The post thinks when you do a sort on it it is technically a write operation to the DataTable object and might not propagate changes made properly or entirely. It is an interesting read and seems to answer my question of why passing valid data to a DataRow is throwing this exception AFTER I make changes to the datatable

UPDATE: Let me be crystal clear. I have already solved my problem. I would just like to know why it is throwing an error. In my view the code should work and it does.. the first run through.

AFTER I have already deleted the column then added it back (run this code once)

When I debug my code line by line in Visiual studio and stop at the line: data.Rows[i].SetField(sortColumnNames[k], value);

  • the row exists
  • the column exisits
  • value is not null
  • sortColumnNames[k] is not null and contains the correct column name
  • i is 0
    Yet it still throws an exception. I would like to know why. What am I missing?

Sorry for the long explanation but this one needs some context unfortunately.

So my problem is this, I have code that sorts data in a DataTable object by column. The user picks the column they want to sort by and then my code sorts it.

I ran into an issue where I needed numbers to sort as numbers not strings (all data in the table is strings). eg (string sorting would result in 1000 coming before 500)

So my solution was to create a temporary column that uses the correct datatype so that numbers get sorted properly and the original string data of the number remains unchanged but is now sorted properly. This worked perfectly. I could sort string numeric data as numeric data without changing the formatting of the number or data type.

I delete the column I used to sort afterwards because I use defaultview to sort and copy data to another DataTable object.

That part all works fine the first time.

The issue is when the user needs to do a different sort on the same column. My code adds back the column. (same name) then tries to add values to the column but then I get a null reference exception "Object not set to an instance of an object"

Here is what I've tried:

  • I've tried using AcceptChanges() after deleting a column but this did nothing.
  • I've tried using column index, name, and column object returned by DataTable.Columns.Add() in the first parameter of SetField() in case it was somehow referencing the "old" column object I deleted (this is what I think the problem is more than likely)
  • I've tried changing the value of the .ItemArray[] directly but this does not work even the first time

Here is the code:

This is the how the column names are passed:

private void SortByColumn()
        {
            if (cbAscDesc.SelectedIndex != -1)//if the user has selected ASC or DESC order
            {
                //clears the datatable object that stores the sorted defaultview
                sortedData.Clear();

                //grabs column names the user has selected to sort by and copies them to a string[]
                string[] lbItems = new string[lbColumnsToSortBy.Items.Count];
                lbColumnsToSortBy.Items.CopyTo(lbItems, 0);

                //adds temp columns to data to sort numerical strings properly
                string[] itemsToSort = AddSortColumns(lbItems);         
                
                //creates parameters for defaultview sort
                string columnsToSortBy = String.Join(",", itemsToSort);
                string sortDirection = cbAscDesc.SelectedItem.ToString();
                data.DefaultView.Sort = columnsToSortBy   " "   sortDirection;

                //copies the defaultview to the sorted table object
                sortedData = data.DefaultView.ToTable();
                RemoveSortColumns(itemsToSort);//removes temp sorting columns
            }
        }

This is where the temp columns are added:

 private string[] AddSortColumns(string[] items)//adds columns to data that will be used to sort
                                                       //(ensures numbers are sorted as numbers and strings are sorted as strings)
        {
            string[] sortColumnNames = new string[items.Length];
            for (int k = 0; k < items.Length; k  )
            {
                int indexOfOrginialColumn = Array.IndexOf(columns, items[k]);
                Type datatype = CheckDataType(indexOfOrginialColumn);
                if (datatype == typeof(double))
                {
                    sortColumnNames[k] = items[k]   "Sort";

                    data.Columns.Add(sortColumnNames[k], typeof(double)); 

                    for (int i = 0; i < data.Rows.Count; i  )
                    {
                        //these three lines add the values in the original column to the column used to sort formated to the proper datatype
                        NumberStyles styles = NumberStyles.Any;
                        double value = double.Parse(data.Rows[i].Field<string>(indexOfOrginialColumn), styles);
                        bool test = data.Columns.Contains("QtySort");
                        data.Rows[i].SetField(sortColumnNames[k], value);//this is line that throws a null ref exception
                    }
                }
                else
                {
                    sortColumnNames[k] = items[k];
                }
            }
            return sortColumnNames;
        }

This is the code that deletes the columns afterward:

private void RemoveSortColumns(string[] columnsToRemove)
        {
            for (int i = 0; i < columnsToRemove.Length; i  )
            {
                if (columnsToRemove[i].Contains("Sort"))
                {
                    sortedData.Columns.Remove(columnsToRemove[i]); 
                }
            }
        }

NOTE: I've been able to fix the problem by just keeping the column in data and just deleting the column from sortedData as I use .Clear() on the sorted table which seems to ensure the exception is not thrown.

I would still like an answer though as to why this is throwing an exception. If I use .Contains() on the line right before the one where the exception is thrown is says the column exists and returns true and in case anyone is wondering the params sortColumnNames[k] and value are never null either.

CodePudding user response:

Your problem is probably here:

private void RemoveSortColumns()
        {
            for (int i = 0; i < data.Columns.Count; i  )
            {
                if (data.Columns[i].ColumnName.Contains("Sort"))
                {
                    data.Columns.RemoveAt(i);
                    sortedData.Columns.RemoveAt(i);
                }
            }
        }

If you have 2 columns, and the first one matches the if, you will never look at the second.

This is because it will run:

  • i = 0
  • is i < columns.Count which is 2 => yes
  • is col[0].Contains("sort") true => yes
  • remove col[0]
  • i = 1
  • is i < columns.Count which is 1 => no

The solution is to readjust i after the removal

private void RemoveSortColumns()
        {
            for (int i = 0; i < data.Columns.Count; i  )
            {
                if (data.Columns[i].ColumnName.Contains("Sort"))
                {
                    data.Columns.RemoveAt(i);
                    sortedData.Columns.RemoveAt(i);
                    i--;//removed 1 element, go back 1
                }
            }
        }

CodePudding user response:

I fixed my original issue by changing a few lines of code in my SortByColumn() method:

private void SortByColumn()
        {
            if (cbAscDesc.SelectedIndex != -1)//if the user has selected ASC or DESC order
            {
                //clears the datatable object that stores the sorted defaultview
                sortedData.Clear();

                //grabs column names the user has selected to sort by and copies them to a string[]
                string[] lbItems = new string[lbColumnsToSortBy.Items.Count];
                lbColumnsToSortBy.Items.CopyTo(lbItems, 0);

                //adds temp columns to data to sort numerical strings properly
                string[] itemsToSort = AddSortColumns(lbItems);         
                
                //creates parameters for defaultview sort
                string columnsToSortBy = String.Join(",", itemsToSort);
                string sortDirection = cbAscDesc.SelectedItem.ToString();
                DataView userSelectedSort = data.AsDataView();
                userSelectedSort.Sort = columnsToSortBy   " "   sortDirection;

                //copies the defaultview to the sorted table object
                sortedData = userSelectedSort.ToTable();
                RemoveSortColumns(itemsToSort);//removes temp sorting columns
            }
        }

Instead of sorting on data.DefaultView I create a new DataView object and pass data.AsDataView() as it's value then sort on that. Completely gets rid of the issue in my original code. For anyone wondering I still believe it is bug with .DefaultView in the .NET framework that Microsoft will probably never fix. I hope this will help someone with a similar issue in the future.
Here is the link again to where I figured out a solution to my problem.
https://stackoverflow.com/a/5665600

  • Related