How to check DBNull value in Linq query results - c#

I am using the below code and trying to group by Currency and Code. After this I am trying to loop through the result set.
But the issue is while looping through the results, at the end I am getting the below exception on the for each statement:
Object cannot be cast from DBNull to other types.
DataTable dt = new DataTable();
var result = from r in dt.AsEnumerable()
result r by new
{
currency = r.Field<String>("CURRENCY"),
Code = r.Field<String>("CODE")
}
into grp
select new
{
currency = grp.Key.currency,
Code = grp.Key.Code,
amount = grp.Sum(x => Convert.ToDouble(x["AMOUNT"]))
};
foreach (var obj in result)
{
String sCurr =obj.currency;
String Code = obj.Code;
string amount= obj.amount.ToString());
}
Please help me to resolve this issue.

Something like
amount = grp.Sum(x => Convert.ToDouble(x["AMOUNT"] == DBNull.Value ? 0 : x["AMOUNT"]));
If that is the line that is giving you the problem.

The way to find the number of CELLS with DBNulls for a specific column:
int numOfEmptyColA = MyDataTable.AsEnumerable().Where(p=>p.IsNull("ColA")).Count();

This:
amount = grp.Sum(x => Convert.ToDouble(x["AMOUNT"]))
will not work as you expect. If x["AMOUNT"] is DBNull.Value instead of a valid double, the conversion will fail with an exception. Instead, try:
amount = grp.Sum(x.Field<double?>("AMOUNT"))
if you expect that field to be a double. Sum will treat the null values as zero, per MSDN.

I'm not sure which one is getting the error, but you can can compare it to DBNull.Value like so.
String sCurr = obj.currency == DBNull.Value ? "" : obj.currency;

Related

how to get a linq query result to int

below query return a single value eg 50. I want it be assign to int so I can do some calculation with that value. how can I do this
var LTLimit = from a in dbavailability.tACLicenseTypes
where a.License_Type == "Long Term"
select a.Limit;
string AST = "";
I'm not completely clear on what you're asking, but presumably the type of data returned by your Linq query is not int, and you want to convert it to int? If so, simply convert it to an int:
var LTLimit = (from a in dbavailability.tACLicenseTypes
where a.License_Type == "Long Term"
select a.Limit).ToList();
int LTLimitInt = 0
if (!int.TryParse(LTLimit.First(), out LTLimitInt))
{
Console.WritLine("LTLimit is not a number!")
}
Since you've updated your question, here's a solution to convert the returned number to an int, multiply it by 2, then convert the result to a string:
var LTLimit = (from a in dbavailability.tACLicenseTypes
where a.License_Type == "Long Term"
select a.Limit).ToList();
int LTLimitInt = 0;
string multipliedResultStr = string.Empty;
if (!int.TryParse(LTLimit.First(), out LTLimitInt))
{
Console.WritLine("LTLimit is not a number!")
}
else
{
multipliedResult = (LTLimitInt * 2).ToString();
Console.WriteLine(string.Format("Result is {0}, multipliedResult ));
}
Edit;
Corrected code to take first item from list.
The following code is one way to retrieve the first integer returned in your query:
var LTLimit = (from a in dbavailability.tACLicenseTypes
where a.License_Type == "Long Term"
select a.Limit).ToList();
int limit = LTLimit[0];
What is the return type of the query?
If the returned type is of another primative data type like a string, then you can try to convert it to an integer using :
var i = Convert.ToInt32(LTLimit);
Or using :
int.TryParse(LTLimit, out var i);
However, it would be better if the data was stored in the correct type in the first place.
You can use Sum() after where() for value of object property
like this bonus.Where(b => b.Id == x.key && b.RequiredScore.HasValue).Sum(b => b.RequiredScore.Value);

Min within a Lambda on a single Line

Just looking for the nicest way to handle the following line and to keep it to one line...
if (int.TryParse(data.Min(x => x.ItemArray[i].ToString()), out result))
contained in the following routine...
var data = model.Data.AsEnumerable();
for (int i = 0; i < model.Data.Columns.Count; i++)
{
int result;
if (int.TryParse(data.Min(x => x.ItemArray[i].ToString()), out result))
{
model.Minimum.Add(DataTableUtility.MinValue(result));
}
}
This works fine until the ItemArray contains rows with no values (empty objects). Is there is simple way to keep this to a single line and handle this situation?
There are methods defined in the DataRow and DataColumn classes that you really should be using. Assuming each column in your table is a nullable int, you could do this:
var mins =
from row in table.AsEnumerable()
select (from DataColumn c in table.Columns
where !row.IsNull(c)
select row.Field<int>(c)).Min();
If you have mixed types, check the type and only accept ints:
var mins =
from row in table.AsEnumerable()
select (from DataColumn c in table.Columns
where !row.IsNull(c)
where c.DataType == typeof(int)
select row.Field<int>(c)).Min();
If you're actually storing integers as strings... then you have other problems... Then you have to parse it out. Just don't try to be clever. Create a method to try and parse out the values and return a value on success, 0 (or null) otherwise.
int? TryConvertInt32(obj value)
{
try { return Convert.ToInt32(value); } catch { return default(int?); }
}
var mins =
from row in table.AsEnumerable()
select row.ItemArray.Select(v => TryConvertInt32(v) ?? 0).Min();
et cetera...
I realised the conversion to string was a bracket early causing it to min by the string value. After this of course the casting of more complex types such as double needed further handling. Couldn't get it completely on one line, but the working solution (as a hybrid to some of these suggestions is as follows...
var data = model.Data.AsEnumerable();
for (int i = 0; i < model.Data.Columns.Count; i++)
{
int result;
if (int.TryParse(data.Min(x => TryConvertInt32((x.ItemArray[i] is System.DBNull || x.ItemArray[i] == null ? int.MaxValue : x.ItemArray[i]))).ToString(), out result))
{
model.Minimum.Add(DataTableUtility.MinValue(result));
}
}
with static "handler" as suggested above..
private static int? TryConvertInt32(object o)
{
try
{
return Convert.ToInt32(o);
}
catch
{
return default(int?);
}
}
You don't want to do this...
The simple reason, WHY you shouldn't do this?
Because this does not convey intent. What on earth is ItemArray, why are you .ToStringing it? Are you sure you don't want to do a safe cast? What about culture? How should you parse the string "1,234" (on the continent, that is "one point two three four", whilst an english speaker would parse that as "one thousand two hundred and thirty four")?
Are you sure you don't want
var results = from i in Enumerable.Range(0, model.Data.Columns.Count)
where data.Min(x => x.ItemArray.OfType<int>().DefaultIfEmpty())
select temp;
Easy:
if (int.TryParse(data.Min(x => (x.ItemArray[i] ?? int.MaxValue).ToString()), out result))
Just add the biggest value possible for int, as you are looking for min values those will be discarded.
EDIT:
So, if I understand then you will have ANY type checked there, so this is better:
if (int.TryParse(data.Min(x => (x.ItemArray[i] ?? "").ToString()), out result))
In this way on null items TryParse will fail.
EDIT:
Support for dbnull:
if (int.TryParse(data.Min(x => (x.ItemArray[i] is System.DBNull || x.ItemArray[i] == null ? "" : x.ItemArray[i]).ToString()), out result))

LinQ returning no datarow error

In below code, Hours is passed with selected values from the UI as below
Hours = "10.1, 11.2";
If there is no value from UI, these will contain as below.
Hours = "";
Below code is to slipt them & get the value from Database. This gives error if the values are having "".
Error at q.CopyToDataTable() The source contains no DataRows.
public void Populate_Gridview(string Hours)
{
string[] selectedHours = Hours.Split(',');
var q = from a in dt.AsEnumerable()
where selectedHours.Contains(a.Field<double?>("Hours").ToString())
select a;
GridView1.DataSource = q.CopyToDataTable();
GridView1.DataBind();
}
If I have Double array, I cannot slipt them if the input is not having any value i.e. "". I get error Input string was not in a correct format.
Double[] selectedHours = Array.ConvertAll(All.Split(','), Double.Parse);
Please guide on how I can solve this issue.
Try this
var q = from a in dt.AsEnumerable()
where (selectedHours.Contains(a.Field<double?>("Hours").ToString())
|| selectedHours.Length==0)
select a;
First, i would improve the way you are deriving the doubles from the string since your way is error prone and inefficient. You could try this approach:
double[] selectedHours = hours.Split(new[]{','}, StringSplitOptions.RemoveEmptyEntries)
.Select(str => {
str = str.Trim();
double hour;
bool isDouble = double.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out hour);
return new { str, isDouble, hour };
})
.Where(h => h.isDouble)
.Select(h => h.hour)
.ToArray();
var q = from row in dt.AsEnumerable()
join hour in selectedHours
on row.Field<double?>("Hours") equals hour
select row;
var dataSource = dt.Clone(); // empty
if (q.Any())
dataSource = q.CopyToDataTable();
The first issue is that CopyToDataTable only works if the source has some rows in it. Otherwise, it cannot determine the columns to be included in the table. A possible solution is to create an empty destination table with the right columns, and copy the rows to that table:
var destTable = dt.Copy();
destTable.Rows.Clear();
q.CopyToDataTable(destTable, LoadOption.OverwriteChanges);
GridView1.DataSource = destTable;
GridView1.DataBind();
The other issue is that, when called on an empty string, by default Split returns an array with on empty string, hence the error when you call Double.Parse on it. To avoid this, use StringSplitOptions.RemoveEmptyEntries:
string[] splitted = All.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
Double[] selectedHours = Array.ConvertAll(splitted, Double.Parse);

Object reference not set to an instance of an object [C#]

I am getting the following error:
Object reference not set to an instance of an object
I don't know what is the cause of this error and how to solve it, here is my code:
while(dr.Read())
{
string variant = dr.GetString(0);
int size = dr.GetInt32(1);
int quantity = dr.GetInt32(2);
DataRow x = dt.Rows
.Cast<DataRow>()
.Where(r => r["variant_name"].Equals(variant) && r["size"].Equals(size))
.FirstOrDefault();
x["quantity"] = quantity;
}
I'm getting the error on this line -> x["quantity"] = quantity;
I'm wondering why it would give a null value because I checked in my database that it should return exactly one match.
FirstOrDefault returns null if the element is not found and you try to access this object.
On the last line x is null, fix with
if(x != null)
x["quantity"] = quantity;
This happens if your condition (.Where) doesn't yield a match.
At that point the FirstOrDefault return the Default part
and this is a null for a reference object as explained in MSDN
The default value for reference and nullable types is null.
while(dr.Read())
{
string variant = dr.GetString(0);
int size = dr.GetInt32(1);
int quantity = dr.GetInt32(2);
DataRow x = dt.Rows
.Cast<DataRow>()
.Where(r => (r["variant_name"].ToString().Equals(variant) && Convert.ToInt32(r["size"]).Equals(size)))
.FirstOrDefault();
if (x != null)
{
x["quantity"] = quantity;
}
}
Thank you for your answer it helped me a lot. I just changed the line .Where a little bit and now it works for me. Thank you again!

LINQ: Add RowNumber Column

How can the query below be modified to include a column for row number (ie: one-based index of results)?
var myResult = from currRow in someTable
where currRow.someCategory == someCategoryValue
orderby currRow.createdDate descending
select currRow;
EDIT1: I'm looking for the results to be {idx, col1, col2...col-n} not {idx, row}.
EDIT2: The row number should correspond to result rows not the table rows.
EDIT3: I DataBind these results to a GridView. My goal was to add a row number column to the GridView. Perhaps a different approach would be better.
Use the method-syntax where Enumerable.Select has an overload with the index:
var myResult = someTable.Select((r, i) => new { Row = r, Index = i })
.Where(x => x.Row.someCategory == someCategoryValue)
.OrderByDescending(x => x.Row.createdDate);
Note that this approach presumes that you want the original index of the row in the table and not in the filtered result since i select the index before i filter with Where.
EDIT: I'm looking for the results to be {idx, col1, col2...col-n} not
{idx, row}. The row number should correspond to result rows not
the table rows.
Then select the anonymous type with all columns you need:
var myResult = someTable.Where(r => r.someCategory == someCategoryValue)
.OrderByDescending(r => r.createdDate)
.Select((r, i) => new { idx = i, col1 = r.col1, col2 = r.col2, ...col-n = r.ColN });
Use this Select method:
Projects each element of a sequence into a new form by incorporating the element's index.
Example:
var myResult = someTable.Where(currRow => currRow.someCategory == someCategoryValue)
.OrderByDescending(currRow => currRow.createdDate)
.Select((currRow, index) => new {Row = currRow, Index = index + 1});
In response to your edit:
If you want a DataTable as result, you can go the non-Linq way by simply using a DataView and add a additional column afterwards.
someTable.DefaultView.RowFilter = String.Format("someCategory = '{0}'", someCategoryValue);
someTable.DefaultView.Sort = "createdDate";
var resultTable = someTable.DefaultView.ToTable();
resultTable.Columns.Add("Number", typeof(int));
int i = 0;
foreach (DataRow row in resultTable.Rows)
row["Number"] = ++i;
what about?
int i;
var myResult = from currRow in someTable
where currRow.someCategory == someCategoryValue
orderby currRow.createdDate descending
select new {Record = i++, currRow};
Just for fun, here's an alternative to Select with two arguments:
var resultsWithIndexes = myResult.Zip(Enumerable.Range(1, int.MaxValue - 1),
(o, i) => new { Index = i, Result = o });
According to you edit 1. NO, YOU CAN'T Linq returns the table as it is. You can build each column, but you lose the power of mapped entities.
This has been asked multiple times before: How do you add an index field to Linq results
There is no straightforward way if want to keep a flat list of columns (i.e. OP's Edit2) and also want a generic solution that works with any IEnumerable without requiring you to list out the set of expected columns.
However, there is a roundabout way to kinda go about it which is to dump the query results into a DataTable using the ToDataTable() method from here and then add a RowNumber column to that table.
var table = query.ToList().ToDataTable();
table.Columns.Add("RowNum", typeof(int));
int i = 0;
foreach (DataRow row in table.Rows)
row["RowNum"] = ++i;
This would likely cause performance issues with large datasets but it's not insanely slow either. On my machine a dataset with ~6500 rows took 33ms to process.
If your original query returned an anonymous type, then that type definition will get lost in the conversion so you'll lose the static typing on the column names of the resulting IEnumerable when you call table.AsEnumerable(). In other words, instead of being able to write something like table.AsEnumerable().First().RowNum you instead have to write table.AsEnumerable().First()["RowNum"]
However, if you don't care about performance and really want your static typing back, then you can use JSON.NET to convert the DataTable to a json string and then back to a list based on the anonymous type from the original query result. This method requires a placeholder RowNum field to be present in the original query results.
var query = (from currRow in someTable
where currRow.someCategory == someCategoryValue
orderby currRow.createdDate descending
select new { currRow.someCategory, currRow.createdDate, RowNum = -1 }).ToList();
var table = query.ToDataTable();
//Placeholder RowNum column has to already exist in query results
//So not adding a new column, but merely populating it
int i = 0;
foreach (DataRow row in table.Rows)
row["RowNum"] = ++i;
string json = JsonConvert.SerializeObject(table);
var staticallyTypedList = JsonConvert.DeserializeAnonymousType(json, query);
Console.WriteLine(staticallyTypedList.First().RowNum);
This added about 120ms to the processing time for my 6500 item dataset.
It's crazy, but it works.
I know I'm late to the party, but I wanted to show what worked for me.
I have a list of objects, and the object has an integer property on it for "row number"... or in this case, "Sequence Number". This is what I did to populate that field:
myListOfObjects = myListOfObjects.Select((o, i) => { o.SequenceNumber = i; return o; }).ToList();
I was surprised to see that this worked.
This one helped me in my case - Excel sheet extraction. anonymous type
var UploadItemList = ItemMaster.Worksheet().AsEnumerable().Select((x, index) => new
{
Code = x["Code"].Value == null ? "" : x["Code"].Value.ToString().Trim(),
Description = x["Description"].Value == null ? "" : x["Description"].Value.ToString().Trim(),
Unit = x["Unit"].Value == null ? "" : x["Unit"].Value.ToString().Trim(),
Quantity = x["Quantity"].Value == null ? "" : x["Quantity"].Value.ToString().Trim(),
Rate = x["Rate"].Value == null ? "" : x["Rate"].Value.ToString().Trim(),
Amount = x["Amount"].Value == null ? "" : x["Amount"].Value.ToString().Trim(),
RowNumber = index+1
}).ToList();
int Lc = 1;
var Lst = LstItemGrid.GroupBy(item => item.CategoryName)
.Select(group => new { CategoryName = group.Key, Items = group.ToList() ,RowIndex= Lc++ })
.ToList();

Categories