InvalidCastException Error caused by DB row? - c#

I have the following C# code:
private DataSet GetSummaryData(DataSet ds)
{
DataSet dsSum = new DataSet();
DataTable dtSum = new DataTable();
DataTable dataTable = ds.Tables[0];
if (dataTable != null)
{
if (dataTable.Rows.Count > 0)
{
if (dataTable.Columns.Count > 1)
{
dtSum.Columns.Add("Line Number", typeof(int));
dtSum.Columns.Add("Throughput", typeof(int));
dtSum.Columns.Add("Lost Time", typeof(int));
dtSum.Columns.Add("Pounds Made", typeof(int));
dtSum.Columns.Add("Pounds Lost", typeof(int));
dtSum.Columns.Add("Yearly Potential", typeof(int));
//Getting the Subtotal of PoundsMade based on the Line Number column
//C# linq query
var query = from row in dataTable.AsEnumerable()
group row by row.Field<int>("Linenumber") into grp
orderby grp.Key
select new
{
Linenumber = grp.Key,
TotalPoundsMade = grp.Sum(r => r.Field<int>("Pounds Made")),
AvgThroughput = grp.Average(r => r.Field<int>("Throughput")),
TotalLostTime = grp.Sum(r => r.Field<int>("Lost Time")),
AvgPercDown = grp.Average(r => r.Field<int>("% Down")),
TotalPoundsLost = grp.Sum(r => r.Field<int>("Pounds Lost")),
TotalYearlyPotential = grp.Sum(r => r.Field<int>("Yearly Potential")),
};
foreach (var grp in query)
{
dtSum.Rows.Add(grp.Linenumber, grp.TotalPoundsMade,grp.AvgThroughput,grp.TotalLostTime,
grp.AvgPercDown, grp.TotalPoundsLost, grp.TotalYearlyPotential);
string strXML = null;
strXML = strXML + "<set name='" + grp.Linenumber + "' value='" + grp.TotalPoundsMade + "'/>";
}
}
}
}
dsSum.Tables.Add(dtSum);
return dsSum;
}
This code, as you can see, uses Linq to access my database. The SQL is:
SELECT
PDT.LineNumber,
SUM(prdt.PoundsMade) as 'Pounds Made',
CAST(ROUND(SUM(CAST(prdt.PoundsMade as DECIMAL))/ (MIN(LSA.AvailableHRS) - SUM(PDT.DownTimeHrs)),0,0) as int)
as 'Throughput',
SUM(PDT.DownTimeHrs) as 'Lost Time',
Str(ROUND(CAST(SUM(PDT.DownTimeHrs) as DECIMAL)/CAST(MIN(LSA.AvailableHRS) as DECIMAL) * 100,0), 3,0) + '%'
as '% Down',
CAST((ROUND(SUM(CAST(prdt.PoundsMade as DECIMAL))/ (MIN(LSA.AvailableHRS) - SUM(PDT.DownTimeHrs)),0,0)) *
(SUM(PDT.DownTimeHrs)) as int) as 'Pounds Lost',
CAST(ROUND(SUM(CAST(prdt.PoundsMade as DECIMAL))/ (MIN(LSA.AvailableHRS) - SUM(PDT.DownTimeHrs)),0,0) as int) *
24 * 365 as 'Yearly Potential'
FROM
rpt_Line_Shift_ProdDownTime AS PDT
LEFT OUTER JOIN rpt_Line_Shift_Prod AS Prdt
ON PDT.LineNumber = Prdt.LineNumber
and PDT.ShiftNumber = Prdt.ShiftNumber
and PDT.WorkDate = Prdt.WorkDate
INNER JOIN rpt_Line_Shift_AvailableHrs AS LSA
ON PDT.LineNumber = LSA.LineNumber
and PDT.ShiftNumber = LSA.ShiftNumber
WHERE
PDT.WorkDate BETWEEN #p_From_Date and #p_Through_Date
GROUP BY
PDT.LineNumber, PDT.ShiftNumber
ORDER BY
PDT.LineNumber, PDT.ShiftNumber
The hangup seems to be that the '% Down' row is not casting correctly. It is a decimal type in SQL, and it makes sense that it would therefore be castable to type int in C#. Unfortunately, when I run the program, I receive a "InvalidCastException" message. Note that, if I comment out the following C# snippets:
"AvgPercDown = grp.Average(r => r.Field("% Down"))," and "grp.AvgPercDown,"
the code "works". Of course, that's not a fix. /shrug
Any thoughts? How can I fix this? I have tried casting the '% Down' row (in the C# code) as something other than int (such as double), but it doesn't like that.

Actually, it has been my experience that Oracle decimal does not correctly convert to C# Int, when using OracleDataReaders I have to get the value as a decimal and convert that to Int. Some newer versions of the ODAC seem to have corrected parts of this issue.

looks like "% Down" is a decimal value. In which case you should use:
AvgPercDown = grp.Average(r => r.Field<decimal>("% Down")),

Related

Date selection comparison with Date_Time data column using Linq query in ASP.NET Gridview

I need my Linq query to compare Date_Time (has only date in data not date_time, datatype - nvarchar) column with date selection of Date1.Value (string) but without showing any error my Gridview doesn't get populated. If I remove the Date_Time where condition, the page runs well. I would be really thankful if someone could help me out with this.
Regards,
Tejas
private void populateData()
{
// here code for populate data
using (TrialEntities13 dc = new TrialEntities13())
{
var v = (from p in dc.Vehicle_Andon
where (p.Line_Name == DropDownList1.SelectedValue
&& p.Loss_Type == 9 && p.Date_Time == Date1.Value)
join s in dc.LOSS_TYPE_MASTER on p.Loss_Description equals s.Loss_Description
select new
{
p,
s.Loss_Description,
});
List<Vehicle_Andon> allP = new List<Vehicle_Andon>();
foreach (var i in v)
{
Vehicle_Andon p = new Vehicle_Andon();
p = i.p;
p.Loss_Description = i.Loss_Description;
allP.Add(p);
}
GridView1.DataSource = allP;
GridView1.DataBind();
}
}

Exploding a lambda expression

The code below works for me however, I would like to add a condition before it is added to the table. What I need is - if the "Scan Time" is between two dates, then it should be added to the "table" if not, then it should be disregarded.
This is for selecting the file..
private void btnSelectFile_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog()
{
Title = "Select the file to open.",
Filter = "DAT (*.dat)|*.dat|TXT (*.txt)|*.txt|All Files (*.*)|*.*",
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
};
if (ofd.ShowDialog() == DialogResult.OK)
{
txtFilePath.Text = ofd.FileName;
loadDataToGridview();
}
}
This is for reading the file then adding it to the datagridview
private void loadDataToGridview()
{
DataTable table = new DataTable();
table.Columns.Add("Emp ID");
table.Columns.Add("Scan Time");
table.Columns.Add("Undefined 1");
table.Columns.Add("Undefined 2");
table.Columns.Add("Undefined 3");
table.Columns.Add("Undefined 4");
var lines = File.ReadAllLines(txtFilePath.Text).ToList();
lines.ForEach(line => table.Rows.Add(line.Split((char)9)));
return table;
}
I got the loadDataToGridview method from here but I do not know how to explode the
lines.ForEach(line => table.Rows.Add(line.Split((char)9)));
lambda expression to include the condition that I need. Let's assume that the name of the datepickers are dateFrom and dateTo.
Your help is greatly appreciated.
Do not use ReadAllLines method because it will load the entire file into memory. In other words, why load the entire file if only 1 line is between your dates.
Use the ReadLines method instead. Why? See my answer here.
var lines = File.ReadLines("").Select(x => Split(x)).Where(x => IsBetweenDates(x[1]));
lines.ForEach(row => table.Rows.Add(row));
dataGridView1.DataSource = table;
You should add your own error handling here as per your needs. I have added a few for you:
private bool IsBetweenDates(string value)
{
var dateValue = DateTime.ParseExact(value, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
return dateValue >= fromDate.Value && dateValue <= toDate.Value;
}
private string[] Split(string line)
{
if (string.IsNullOrWhitespace(x))
{
// There is nothing in this line. Is this allowed in your case?
// If yes do whatever you need to do here. For example, log it or something.
}
var splits = line.Split((char)9);
if (splits.Length != 6)
{
// This line does not have 6 fields so what do you want to do?
}
return splits;
}
Use Where() as suggested by #CodingYoshi, but split the line first (so you don't have to do it twice), using a Select() statement:
var lines = File.ReadAllLines(txtFilePath.Text).Select(line => line.Split(';')).Where(fields => fields[1] >= fromDate && fields[1] <= toDate).ToList();
lines.ForEach(row => table.Rows.Add(row));
You may also want to consider using something like CsvHelper instead of parsing the file manually
You can probably also use Select Method which in turns gets an array of all DataRow objects that match the filter criteria.
DataTable table = new DataTable();
table.Columns.Add("Emp ID");
//Add All your columns
var lines = File.ReadAllLines(txtFilePath.Text).ToList();
lines.ForEach(line => table.Rows.Add(line.Split((char)9)));
//Till the data has been already there in your DataTable.
//Create a new DataTable for Filtered Records.
DataTable FilteredTable = new DataTable();
//The Statement works like a SQL Statement which is equal to
//Select * from TableName Where DateColumn Between two dates.
DataRow[] rows = table.Select("date >= #" + from_date + "# AND date <= #" + to_date + "#");
//Now add all rows to the new Table.
foreach (DataRow dr in rows)
{
FilteredTable.ImportRow(dr);
}
dataGridView1.DataSource = FilteredTable;
if from_date and to_date is a DateTime and not a string, you need to use the ToString(your date format) to get the correct sql statement

Eliminate comma(,) from a column of a Data Table using LINQ

I have a DataTable as shown below:
After using below LINQ Expression on above DT:
if (dt.AsEnumerable().All(row => string.IsNullOrEmpty(row.Field<string>("SameReferences"))))
BindOldReferences(dt);
else
{
var grps = from row in dt.AsEnumerable()
let RefID = row.Field<string>("ReferenceID")
let RefDescription = row.Field<string>("ReferenceDescription")
let ReferenceUrl = row.Field<string>("ReferenceUrl")
let SortOrder = row.Field<int>("sortOrder")
group row by new { RefDescription, ReferenceUrl, SortOrder } into groups
select groups;
dt = grps.Select(g =>
{
DataRow first = g.First();
if (first.Field<string>("SameReferences") != null)
{
string duplicate = first.Field<int>("SortOrder").ToString();
first.SetField("SameReferences", string.Format("{0},{1}", duplicate, first.Field<string>("SameReferences")));
}
return first;
}).CopyToDataTable();
}
After applying above LINQ to DT it becomes :
Expected DT as below : eliminate (,) comma when there is single value in column Samereferences. So what changes i have to make to LINQ to get the expected below output.
Please help..!
You can use String.Trim method like this:-
first.SetField("SameReferences", string.Format("{0},{1}", duplicate,
first.Field<string>("SameReferences")).Trim(','));
It will remove all the trailing comma.
Try this:
if (first.Field<string>("SameReferences") != null)
{
string duplicate = first.Field<int>("SortOrder").ToString();
string sameReference = first.Field<string>("SameReferences");
if (String.IsNullOrEmpty(sameReference))
first.SetField("SameReferences", duplicate);
else
first.SetField("SameReferences", string.Format("{0},{1}", duplicate, sameReference));
}

Use linq to find DataTable(Name) in a DataSet using unique list of Column Names

I got roped into some old code, that uses loose (untyped) datasets all over the place.
I'm trying to write a helper method to find the DataTable.Name using the names of some columns.....(because the original code has checks for "sometimes we have 2 datatables in a dataset, sometimes 3, sometimes 4)..and its hard to know the order. Basically, the TSQL Select statements conditionally run. (Gaaaaaaaaaaaaaahhh).
Anyway. I wrote the below, and if I give it 2 column names, its matching on "any" columnname, not "all column names".
Its probably my linq skillz (again), and probably a simple fix.
But I've tried to get the syntax sugar down..below is one of the things I wrote, that compiles.
private static void DataTableFindStuff()
{
DataSet ds = new DataSet();
DataTable dt1 = new DataTable("TableOne");
dt1.Columns.Add("Table1Column11");
dt1.Columns.Add("Name");
dt1.Columns.Add("Age");
dt1.Columns.Add("Height");
DataRow row1a = dt1.NewRow();
row1a["Table1Column11"] = "Table1Column11_ValueA";
row1a["Name"] = "Table1_Name_NameA";
row1a["Age"] = "AgeA";
row1a["Height"] = "HeightA";
dt1.Rows.Add(row1a);
DataRow row1b = dt1.NewRow();
row1b["Table1Column11"] = "Table1Column11_ValueB";
row1b["Name"] = "Table1_Name_NameB";
row1b["Age"] = "AgeB";
row1b["Height"] = "HeightB";
dt1.Rows.Add(row1b);
ds.Tables.Add(dt1);
DataTable dt2 = new DataTable("TableTwo");
dt2.Columns.Add("Table2Column21");
dt2.Columns.Add("Name");
dt2.Columns.Add("BirthCity");
dt2.Columns.Add("BirthState");
DataRow row2a = dt2.NewRow();
row2a["Table2Column21"] = "Table2Column1_ValueG";
row2a["Name"] = "Table2_Name_NameG";
row2a["BirthCity"] = "BirthCityA";
row2a["BirthState"] = "BirthStateA";
dt2.Rows.Add(row2a);
DataRow row2b = dt2.NewRow();
row2b["Table2Column21"] = "Table2Column1_ValueH";
row2b["Name"] = "Table2_Name_NameH";
row2b["BirthCity"] = "BirthCityB";
row2b["BirthState"] = "BirthStateB";
dt2.Rows.Add(row2b);
ds.Tables.Add(dt2);
DataTable dt3 = new DataTable("TableThree");
dt3.Columns.Add("Table3Column31");
dt3.Columns.Add("Name");
dt3.Columns.Add("Price");
dt3.Columns.Add("QuantityOnHand");
DataRow row3a = dt3.NewRow();
row3a["Table3Column31"] = "Table3Column31_ValueM";
row3a["Name"] = "Table3_Name_Name00M";
row3a["Price"] = "PriceA";
row3a["QuantityOnHand"] = "QuantityOnHandA";
dt3.Rows.Add(row3a);
DataRow row3b = dt3.NewRow();
row3b["Table3Column31"] = "Table3Column31_ValueN";
row3b["Name"] = "Table3_Name_Name00N";
row3b["Price"] = "PriceB";
row3b["QuantityOnHand"] = "QuantityOnHandB";
dt3.Rows.Add(row3b);
ds.Tables.Add(dt3);
string foundDataTable1Name = FindDataTableName(ds, new List<string> { "Table1Column11", "Name" });
/* foundDataTable1Name should be 'TableOne' */
string foundDataTable2Name = FindDataTableName(ds, new List<string> { "Table2Column21", "Name" });
/* foundDataTable1Name should be 'TableTwo' */
string foundDataTable3Name = FindDataTableName(ds, new List<string> { "Table3Column31", "Name" });
/* foundDataTable1Name should be 'TableThree' */
string foundDataTableThrowsExceptionName = FindDataTableName(ds, new List<string> { "Name" });
/* show throw exception as 'Name' is in multiple (distinct) tables */
}
public static string FindDataTableName(DataSet ds, List<string> columnNames)
{
string returnValue = string.Empty;
DataTable foundDataTable = FindDataTable(ds, columnNames);
if (null != foundDataTable)
{
returnValue = foundDataTable.TableName;
}
return returnValue;
}
public static DataTable FindDataTable(DataSet ds, List<string> columnNames)
{
DataTable returnItem = null;
if (null == ds || null == columnNames)
{
return null;
}
List<DataTable> tables =
ds.Tables
.Cast<DataTable>()
.SelectMany
(t => t.Columns.Cast<DataColumn>()
.Where(c => columnNames.Contains(c.ColumnName))
)
.Select(c => c.Table).Distinct().ToList();
if (null != tables)
{
if (tables.Count <= 1)
{
returnItem = tables.FirstOrDefault();
}
else
{
throw new IndexOutOfRangeException(string.Format("FindDataTable found more than one matching Table based on the input column names. ({0})", String.Join(", ", columnNames.ToArray())));
}
}
return returnItem;
}
I tried this too (to no avail) (always has 0 matches)
List<DataTable> tables =
ds.Tables
.Cast<DataTable>()
.Where
(t => t.Columns.Cast<DataColumn>()
.All(c => columnNames.Contains(c.ColumnName))
)
.Distinct().ToList();
To me sounds like you're trying to see if columnNames passed to the method are contained within Column's name collection of Table. If that's the case, this should do the work.
List<DataTable> tables =
ds.Tables
.Cast<DataTable>()
.Where(dt => !columnNames.Except(dt.Columns.Select(c => c.Name)).Any())
.ToList();
(Below is an append by the asker of the question)
Well, I had to tweak it to make it compile, but you got me there..
Thanks.
Final Answer:
List<DataTable> tables =
ds.Tables.Cast<DataTable>()
.Where
(dt => !columnNames.Except(dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName))
.Any()
)
.ToList();
Final Answer (which is not case sensitive):
List<DataTable> tables =
ds.Tables.Cast<DataTable>()
.Where
(dt => !columnNames.Except(dt.Columns.Cast<DataColumn>()
.Select(c => c.ColumnName), StringComparer.OrdinalIgnoreCase)
.Any()
)
.ToList();

DataTable.Select string function in where clause

I'm having problems with a DataTable.Select() where the matching values might contain leading spaces and need to be trimmed correctly to return the correct amount of records.
Currently my code is returning less records as the matching fails because of unwanted characters.
How do you handle DataTable.Select as the example SQL below suggests?
SELECT * FROM Table WHERE LTRIM(FullName) = ' Joe Smith'
I' tried
dataTable.Select("LTRIM(FullName) = ' Joe Smith'");
but it failed.
Any ideas?
I would suggest to use Linq-To-DataSet instead, it makes it a lot clearer and easier to maintain:
var rows = from row in dataTable.AsEnumerable()
where row.Field<string>("FullName").Trim() == "Joe Smith"
select row;
If you want to use LTRIM instead, you just have to replace Trim with TrimStart.
if you want an array or list, use ToArray or ToList, e.g.
DataRow[] rowsArray = rows.ToArray();
or a DataTable
dataTable = rows.CopyToDataTable();
Edit: if you insist on using DataTable.Select or you can't use linq, this should work(LTRIM is not supported):
rowsArray = dataTable.Select("TRIM(FullName) = 'Joe Smith'");
Give this a try:
string searchTerm = " Joe Smith";
string expression = String.Format("TRIM(FullName) = '{0}'", searchTerm.Trim());
dataTable.Select(expression);
DataTable excelData = objGetExcelData.DataExcel(objEntities.StrFilePath, ConfigSettings.GetAppConfigValue("select * from sheet1"));
StringBuilder strInput = new StringBuilder();
DataView view = new DataView(excelData);
DataTable distinctValues = view.ToTable(true, "GROUP_NAME");
if (distinctValues.Rows.Count > 0)
{
for (int i = 0; i < distinctValues.Rows.Count; i++)
{
strGroupName = Convert.ToString(distinctValues.Rows[i]["GROUP_NAME"]);
foreach (DataRow item in excelData.Select("GROUP_NAME = '" + strGroupName + "'"))
{
strInput.Append(Convert.ToString(item[0]));
strInput.Append("~");
strInput.Append(Convert.ToString(item[1]));
strInput.Append(",");
strDasID = Convert.ToString(item[0]);
}
}
}

Categories

Resources