There are a lot of questions on SO about 'system.invalidoperationexception
' but none of them have helped me figure this out.
I have a datatable
( tab2tableExtra
) that gets its values from another datatable
( tab2table
), which in turn gets is values from a database. tab2tableExtra
only contains 4 rows and 8 columns and all its values (barring column [0]) are calculated from tab2table
. Specifically two columns in tab2table
are used to drive a for loop and those seem to be at the crux of the problem. These are MLevel
and RLevel
, which are shown in the code below.
Everything works perfectly when both the MLevel
column and RLevel
column have some non-null values. But if one of those columns is all nulls ( DBNull.Value
-which can happen on occasion), then something seems to go wrong. Interestingly, it doesn't actually cause the code to break, but it seems to mess with the calculated values in tab2tableExtra
, as the entire table is null (except for column [0], which is just 1, 2, 3, 4).
Upon debugging, when either MLevel
or RLevel
(but not both) contains all null I see a 'system.invalidoperationexception from
this`, as shown in the figure below. What causes the code to do is break out of the for loop after one iteration, and then not perform any of the rest of calculations.
This message comes from the following code:
// Add values to tab2tableExtra
// Add Levels columns
DataRow row = tab2tableExtra.NewRow();
for (int i = 1; i < 5; i++)
{
row = tab2tableExtra.NewRow();
row["Level"] = i;
tab2tableExtra.Rows.Add(row);
}
// Add participation rate column values
DataRow dr = tab2tableExtra.Rows[0];
int rowCount = tab2table.Rows.Count;
/*int countnumM = tab2table.AsEnumerable().Where(x => int.Parse(x["MLevel"].ToString()) == 1 ||
int.Parse(x["MLevel"].ToString()) == 2 || int.Parse(x["MLevel"].ToString()) == 3 ||
int.Parse(x["MLevel"].ToString()) == 4).ToList().Count;*/
int countnumM = tab2table.AsEnumerable().Select(x => int.TryParse(x["MLevel"].ToString(), out var d) ? d : (int?)null).Where(x => x >= 1 && x <= 4).Count();
int countnumRW = tab2table.AsEnumerable().Select(x => int.TryParse(x["RLevel"].ToString(), out var d) ? d : (int?)null).Where(x => x >= 1 && x <= 4).Count();
/*int countnumRW = tab2table.AsEnumerable().Where(x => int.Parse(x["RLevel"].ToString()) == 1 || int.Parse(x["RLevel"].ToString()) == 2 ||
int.Parse(x["RLevel"].ToString()) == 3 || int.Parse(x["RLevel"].ToString()) == 4).ToList().Count;*/
for (int i = 1; i < 5; i++)
{
if (countnumM > 0)
{
float levelPercM = Convert.ToInt32(tab2table.Compute("COUNT(MLevel)", "MLevel =" + i.ToString()));
tab2tableExtra.Rows[i - 1][1] = Math.Round(100 * levelPercM / countnumM, 2);
}
else
tab2tableExtra.Rows[i - 1][1] = null;
if (countnumRW > 0)
{
decimal levelPercRW = Convert.ToDecimal(tab2table.Compute("COUNT([RLevel])", "RLevel =" + i.ToString()));
tab2tableExtra.Rows[i - 1][2] = Math.Round(100 * levelPercRW / countnumRW, 2);
}
else
tab2tableExtra.Rows[i - 1][2] = null;
}
// Add the rest of the column values that only require a single number
tab2tableExtra.Rows[0][3] = rowCount;
if (countnumM > 0)
tab2tableExtra.Rows[0][4] = 100*countnumM/rowCount;
else
tab2tableExtra.Rows[0][4] = null;
if (countnumRW > 0)
tab2tableExtra.Rows[0][5] = 100*countnumRW/rowCount;
else
tab2tableExtra.Rows[0][5] = null;
decimal RWavg = Convert.ToDecimal(tab2table.Compute("AVG([ROverall])", ""));
decimal Mavg = Convert.ToDecimal(tab2table.Compute("AVG([MOverall])", ""));
tab2tableExtra.Rows[0][6] = RWavg;
tab2tableExtra.Rows[0][7] = Mavg;
For reference, when I debug with a tab2table
that has at least one non-null value in both MLevel
and RLevel
, I see this:
If anyone knows how to solve this, or even just why this happening, it would be very useful.
In these lines of code:
int countnumM = tab2table.AsEnumerable()
.Select(x => int.TryParse(x["MLevel"].ToString(), out var d) ? d : (int?)null)
.Where(x => x >= 1 && x <= 4).Count();
int countnumRW = tab2table.AsEnumerable()
.Select(x => int.TryParse(x["RLevel"].ToString(), out var d) ? d : (int?)null)
.Where(x => x >= 1 && x <= 4).Count();
You're using int.TryParse
to read the value of x["MLevel"]
.
If x["MLevel"].ToString()
doesn't return a string
that can be parsed as an int
then int.TryParse
will return false. That includes if x
is DBNull.Value
. That won't get parsed as an int
.
Then, after the TryParse
, you have this: ? d : (int?)null)
? d : (int?)null)
In other words, if TryParse
returned true - it was able to parse, then you're selecting d
- the parsed value.
But if it couldn't parse the value - TryParse
returned false - then you're returning (int?)null
- which is effectively null.
At the end you're filtering the results for values between 1 and 4, and then counting the number of those results:
.Where(x => x >= 1 && x <= 4).Count();
Just as you described, if there were some non-null values then that Count
will probably return one or more. But if there are all null values then .Count
will equal 0, because no values were between 1 and 4.
In that case countnumM
and/or countnumRW
would equal 0.
Further down you're setting some additional values if countnumM > 0
and if countnumRW > 0
. But they're not greater than 0. They are 0. If they are 0, you're code will do what you expect:
for (int i = 1; i < 5; i++)
{
if (countnumM > 0) // this is == 0
{
float levelPercM = Convert.ToInt32(tab2table.Compute("COUNT(MLevel)", "MLevel =" + i.ToString()));
tab2tableExtra.Rows[i - 1][1] = Math.Round(100 * levelPercM / countnumM, 2);
}
else // This is what's happening
tab2tableExtra.Rows[i - 1][1] = null;
if (countnumRW > 0) // this is == 0
{
decimal levelPercRW = Convert.ToDecimal(tab2table.Compute("COUNT([RLevel])", "RLevel =" + i.ToString()));
tab2tableExtra.Rows[i - 1][2] = Math.Round(100 * levelPercRW / countnumRW, 2);
}
else // This is what's happening
tab2tableExtra.Rows[i - 1][2] = null;
}
To clarify around the other part - the InvalidOperationException
:
When you're in the debugger and you inspect the properties of a variable, it's going to try to display the value of each property. That's what the list of properties is in your image - it's all the properties of this
.
Some of them might be properties you don't use or care about, but when it tries to read the properties so it can show them to you, the property throws an exception. So now instead of a property value it shows you the exception it got when trying to read the property.
This is understandably confusing because you're debugging trying to figure out what's going on in your code, and then you see exceptions. You wonder if they might be related to the problem.
Generally they aren't because if your code throws an exception then it stops execution of your code. That's an exception you would need to figure out. But if it doesn't stop your code from running and it's a property on some framework class (like Form.AccessibilityObject
) that you aren't even using, then you can usually ignore it.
As Scott Hannen said, the problem here has nothing to do with the 'system.invalidoperationexception'
exception. For anyone who comes across a similar problem, the actually issue was with the null
.
It turns out, in C# datatables cannot handle null
values. And instead of giving an error, it simply just stops filling the datatable once it encounters one. In order to fix this, one has to make sure to use DBNull.Value
. So in the code in question, for example, changing all null
to DBNull.Value
will fix the issue.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.