Specified cast is not valid with field int - c#

I keep getting the "Specified cast is not valid" error on my code in the ID column. I tried using
data.Columns.Add("ID", typeof(int));
to let it know that the ID is an int. I am getting the data from a local .xls file.
here's my code:
foreach (DataRow p in data.Rows)
{
TopPlayed top = new TopPlayed()
{
TrackID = p.Field<int>("ID"),
TrackName = p.Field<string>("Track Name"),
ArtistName = p.Field<string>("Artist Name"),
Times = p.Field<double>("NoOfPlays").ToString()
};
data.Columns.Add("ID", typeof(int));
daa.Add(top);
}
here's my class:
public class TopPlayed
{
public int TrackID { get; set; }
public string TrackName { get; set; }
public string ArtistName { get; set; }
public string Times { get; set; }
}
cheers in advance :)

Try using double instead. Excel exposes data as either strings or doubles.
TopPlayed top = new TopPlayed()
{
TrackID = Convert.ToInt32(p.Field<double>("ID")),
TrackName = p.Field<string>("Track Name"),
ArtistName = p.Field<string>("Artist Name"),
Times = p.Field<double>("NoOfPlays").ToString()
};

Another way is checking our type (with Watch window for example):
row.Table.Columns["id"].DataType
What print (in my case): {Name = "Int64" FullName = "System.Int64"}, then change your code to proper type:
TopPlayed top = new TopPlayed()
{
TrackID = p.Field<Int64>("ID"),
TrackName = p.Field<string>("Track Name"),
ArtistName = p.Field<string>("Artist Name"),
Times = p.Field<double>("NoOfPlays").ToString()
};
public class TopPlayed
{
public Int64 TrackID { get; set; }
public string TrackName { get; set; }
public string ArtistName { get; set; }
public string Times { get; set; }
}

If it's a query inside a query, the best way that worked is to submit a simple String
and then to convert it to int32 ex.:
instead of using
p.Field("ID")
do it
row["ID"]

Related

C# Custom bind in DataGridView (WinForm)

I have three classes
public class FeatureBook
{
public string Id { get; set; }
public String name { get; set; }
public String type { get; set; }
}
public class Feature
{
public String feature_id { get; set; }
public String value { get; set; }
}
public class Human {
public string Id { get; set; }
public bool validated { get; set; }
public List<Feature> features { get; set; }
public override String ToString() => Id;
}
Human has List<Feature>. Feature is linked to FeatureBook by feature_id
And I have DataGridView.
How can I get something like in the picture:
First of all, for such of functionality, i'd use 2 datagridviews with master-detail relationship.
Second of all, if you would like to bind data to single datagridview, you need to convert Feature's rows into columns. Here is complete sample (created with LinqPad):
void Main()
{
//create human list
List<Human> people = new List<Human>()
{
new Human(){Id = "H1", validated =false, features = new List<Feature>()
{
new Feature(){feature_id = "H1F1", value ="Feature 1"},
new Feature(){feature_id = "H1F2", value ="Feature 2"}
}},
new Human(){Id = "H2", validated =false, features = new List<Feature>()
{
new Feature(){feature_id = "H2F1", value ="Feature 1"},
new Feature(){feature_id = "H2F2", value ="Feature 2"},
new Feature(){feature_id = "H2F3", value ="Feature 3"},
new Feature(){feature_id = "H2F4", value ="Feature 4"},
new Feature(){feature_id = "H2F5", value ="Feature 5"}
}}
};
//create datatable
DataTable dt = new DataTable();
//add known columns (related to Human)
dt.Columns.AddRange(new DataColumn[]
{
new DataColumn("Id", typeof(string)),
new DataColumn("validated", typeof(string))
});
//get max. of futures
int fc = people.Select(x=>x.features.Count).Max();
//add columns related to Feature
for(int i=0; i<fc; i++)
dt.Columns.Add(new DataColumn($"Feature {i}"));
//add data to datatable
foreach(Human h in people)
{
//add Human details
DataRow dr = dt.NewRow();
dr["Id"] = h.Id;
dr["validated"] = h.validated;
//add Feature details
for(int i=0; i<h.features.Count; i++)
{
Feature f = h.features[i];
dr[$"Feature {i}"] = f.value;
}
dt.Rows.Add(dr);
}
//datatable is ready to use
//dump its content ;)
dt.Dump();
}
// Define other methods and classes here
public class FeatureBook
{
public string Id { get; set; }
public string name { get; set; }
public string type { get; set; }
}
public class Feature
{
public string feature_id { get; set; }
public string value { get; set; }
}
public class Human
{
public string Id { get; set; }
public bool validated { get; set; }
public List<Feature> features { get; set; }
public override string ToString() => Id;
}
Note: there's few other ways to achieve that, but i wanted to show you the simplest way ;)

How to use access all rows of type List<T> in C# .NET Framework?

I have a Model called SalesItemModel which has properties such as SaleItemId, SalesID, ProductID, Quantity, and UnitPrice. I pull list from database using SalesItemService.ListSalesItemsDataBySaleID(salesID).
public class SalesItemModel
{
public int SalesItemID { get; set; }
public int SalesID { get; set; }
public int ProductID { get; set; }
public string ProductName { get; set; }
public Nullable<decimal> UnitPrice { get; set; }
public double Quantity { get; set; }
}
Pulling Sales Items:
var salesItem = SalesItemService.ListSalesItemsDataBySaleID(SalesID);
And I want to access quantity and product ID of every row of the returned list to edit data on stock table using those data.
If you just want to access the data in list, you can try the below loop.
foreach(var item in salesItem)
{
item.Quantity = 10;
item.ProductId = 20;
}
var tests = new List<Test>
{
new Test{Name = "Abc", Age = 1},
new Test{Name = "Abc2", Age = 12},
new Test{Name = "Abc3", Age = 13},
new Test{Name = "Abc4", Age = 14},
};
tests = tests.Select(x=>
{
x.Age = 2;
return x;
}).ToList();
Example which you can use.

Read hierarchical XML and flatten into a List of objects?

I have an XML document which I would like to read, flatten, and return a List<> of an object I called PMRow. For each CodingRow in the XML there should be a corresponding List element and the "header" details of the Invoice should simply repeat. Many of the XML elements will be ignored in this transformation at both the Invoice and CodingRow level. I've started writing the code below and I can't conceptualize where to go from there:
Incomplete code:
public static List<PMRow> ParseToPMRows(FileInfo myFile)
{
var xDoc = XDocument.Load(myFile.FullName);
var query = from element in xDoc.Element("InvoiceDocument").Element("Invoice").Element("CodingRows").Elements("CodingRow")
select
}
Target Class:
public class PMRow
{
public string SupplierCode { get; set; }
public string InvoiceNumber { get; set; }
public DateTime InvoiceDate { get; set; }
public decimal GrossSum { get; set; }
public decimal NetSum { get; set; }
public decimal TaxSum { get; set; }
public decimal CR_GrossSum { get; set; }
public string AccountNumber { get; set; }
public string DimCode1 { get; set; }
}
XML Docuemnt:
<InvoiceDocument>
<Method>Post</Method>
<Invoice>
<GrossSum nil="False">700.000000</GrossSum>
<InvoiceDate nil="False">2018-09-26</InvoiceDate>
<InvoiceNumber nil="False">180928003802901</InvoiceNumber>
<NetSum nil="False">700.000000</NetSum>
<PaidSum nil="False">0.000000</PaidSum>
<PaymentBlock nil="False">false</PaymentBlock>
<PaymentDate nil="False">0001-01-01</PaymentDate>
<SupplierCode nil="False">AQUINC</SupplierCode>
<SupplierParentId nil="False"></SupplierParentId>
<TaxCode nil="False"></TaxCode>
<TaxPercent nil="False">0.000000</TaxPercent>
<TaxPercent2 nil="False">0.000000</TaxPercent2>
<TaxSum nil="False">0.000000</TaxSum>
<OrderNumber nil="False"></OrderNumber>
<OrderInCoding nil="False" />
<CodingRows>
<CodingRow>
<GrossSum nil="False">500.000000</GrossSum>
<InternalStatus nil="False">Loaded</InternalStatus>
<AccountCode nil="False">1990</AccountCode>
<AccountName nil="False">Gain on Non-Operating Asset</AccountName>
<DimCode1 nil="False">01</DimCode1>
<DimName1 nil="False">Operating/Unrestricted</DimName1>
<MaterialGroup nil="False"></MaterialGroup>
<FiscalYear nil="False"></FiscalYear>
<DimCode3 nil="False">06</DimCode3>
<DimName3 nil="False">Sports</DimName3>
<DimCode4 nil="False">06500</DimCode4>
<DimName4 nil="False">Personal Training</DimName4>
<DimCode5 nil="False">6</DimCode5>
<DimName5 nil="False">Minneapolis</DimName5>
<DimCode6 nil="False"></DimCode6>
<DimName6 nil="False"></DimName6>
</CodingRow>
<CodingRow>
<GrossSum nil="False">200.000000</GrossSum>
<InternalStatus nil="False">Loaded</InternalStatus>
<AccountCode nil="False">2390</AccountCode>
<AccountName nil="False">Gain on Non-Operating Asset</AccountName>
<DimCode1 nil="False">02</DimCode1>
<DimName1 nil="False">Operating/Unrestricted</DimName1>
<MaterialGroup nil="False"></MaterialGroup>
<FiscalYear nil="False"></FiscalYear>
<DimCode3 nil="False">06</DimCode3>
<DimName3 nil="False">Sports</DimName3>
<DimCode4 nil="False">06500</DimCode4>
<DimName4 nil="False">Personal Training</DimName4>
<DimCode5 nil="False">6</DimCode5>
<DimName5 nil="False">Minneapolis</DimName5>
<DimCode6 nil="False"></DimCode6>
<DimName6 nil="False"></DimName6>
</CodingRow>
</CodingRows>
<InvoiceRows />
</Invoice>
Conceptualized target (two objects of type PMRow in a List):
AQUINC, 180928003802901, 9/26/2018, 700, 700, 0, 500, 1990, 01
AQUINC, 180928003802901, 9/26/2018, 700, 700, 0, 200, 2390, 02
Is this what you are looking for?
XElement invoice = xDoc.Root.Element("Invoice");
List<PMRow> rows = invoice
.Element("CodingRows")
.Elements("CodingRow")
.Select(codingRow => new PMRow
{
SupplierCode = invoice.Element("SupplierCode").Value,
InvoiceNumber = invoice.Element("InvoiceNumber").Value,
InvoiceDate = DateTime.Parse(invoice.Element("InvoiceDate").Value),
GrossSum = decimal.Parse(invoice.Element("GrossSum").Value),
NetSum = decimal.Parse(invoice.Element("NetSum").Value),
TaxSum = decimal.Parse(invoice.Element("TaxSum").Value),
CR_GrossSum = decimal.Parse(codingRow.Element("GrossSum").Value),
AccountNumber = codingRow.Element("AccountCode").Value,
DimCode1 = codingRow.Element("DimCode1").Value,
})
.ToList();
Note: the above assumes that all elements will be present and valid. If this is not the case, you will need to add appropriate handling for that.
Fiddle: https://dotnetfiddle.net/DjKcDg
I generally find more convenient to first serialize XML into class object and then iterate over serialzed object.
You only need
XmlSerializer serializer = new XmlSerializer(typeof(Invoice));
TextReader reader = new StringReader(source);
Invoice resultObj = (Invoice)serializer.Deserialize(reader);
var pmRows = resultObj.CodingRows.CodingRow.Select(item => new PMRow
{
CR_GrossSum = Convert.ToDecimal(resultObj.GrossSum.Text),
InvoiceDate = Convert.ToDateTime(resultObj.InvoiceDate.Text),
InvoiceNumber = resultObj.InvoiceNumber.Text,
SupplierCode = resultObj.SupplierCode.Text,
NetSum = Convert.ToDecimal(resultObj.NetSum.Text),
GrossSum = Convert.ToDecimal(resultObj.GrossSum.Text),
TaxSum = Convert.ToDecimal(resultObj.TaxSum.Text),
AccountNumber = item.AccountCode.Text,
DimCode1 = item.DimCode1.Text
}).ToList();
you can check my solution from https://dotnetfiddle.net/jr11hB
Try code below. I assumed they are multiple Invoice in a file so I had to use SelectMany() to return a flat list
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication75
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
ParseToPMRows(FILENAME);
}
public static List<PMRow> ParseToPMRows(string myFile)
{
XDocument xDoc = XDocument.Load(myFile);
var tempResults = xDoc.Descendants("Invoice").Select(x => new {
supplierCode = (string)x.Element("SupplierCode"),
invoiceNumber = (string)x.Element("InvoiceNumber"),
invoiceDate = (DateTime)x.Element("InvoiceDate"),
grossSum = (decimal)x.Element("GrossSum"),
netSum = (decimal)x.Element("NetSum"),
taxSum = (decimal)x.Element("TaxSum"),
codingRows = x.Descendants("CodingRow").Select(y => new {
crGrossSum = (decimal)y.Element("GrossSum"),
accounNumber = (string)y.Element("AccountCode"),
dimCode1 = (string)y.Element("DimCode1")
}).ToList()
}).ToList();
List<PMRow> rows = tempResults.Select(x => x.codingRows.Select(y => new PMRow()
{
SupplierCode = x.supplierCode,
InvoiceNumber = x.invoiceNumber,
InvoiceDate = x.invoiceDate,
GrossSum = x.grossSum,
NetSum = x.netSum,
TaxSum = x.taxSum,
CR_GrossSum = y.crGrossSum,
AccountNumber = y.accounNumber,
DimCode1 = y.dimCode1
})).SelectMany(x => x).ToList();
return rows;
}
}
public class PMRow
{
public string SupplierCode { get; set; }
public string InvoiceNumber { get; set; }
public DateTime InvoiceDate { get; set; }
public decimal GrossSum { get; set; }
public decimal NetSum { get; set; }
public decimal TaxSum { get; set; }
public decimal CR_GrossSum { get; set; }
public string AccountNumber { get; set; }
public string DimCode1 { get; set; }
}
}
For one Invoice you can use following :
public static List<PMRow> ParseToPMRows(string myFile)
{
XDocument xDoc = XDocument.Load(myFile);
var tempResults = xDoc.Descendants("Invoice").Select(x => new {
supplierCode = (string)x.Element("SupplierCode"),
invoiceNumber = (string)x.Element("InvoiceNumber"),
invoiceDate = (DateTime)x.Element("InvoiceDate"),
grossSum = (decimal)x.Element("GrossSum"),
netSum = (decimal)x.Element("NetSum"),
taxSum = (decimal)x.Element("TaxSum"),
codingRows = x.Descendants("CodingRow").Select(y => new {
crGrossSum = (decimal)y.Element("GrossSum"),
accounNumber = (string)y.Element("AccountCode"),
dimCode1 = (string)y.Element("DimCode1")
}).ToList()
}).FirstOrDefault();
List<PMRow> rows = tempResults.codingRows.Select(x => new PMRow()
{
SupplierCode = tempResults.supplierCode,
InvoiceNumber = tempResults.invoiceNumber,
InvoiceDate = tempResults.invoiceDate,
GrossSum = tempResults.grossSum,
NetSum = tempResults.netSum,
TaxSum = tempResults.taxSum,
CR_GrossSum = x.crGrossSum,
AccountNumber = x.accounNumber,
DimCode1 = x.dimCode1
}).ToList();
return rows;
}

LinqToCSV format column as Text

I'm using LinqToCSV to output a List to CSV. Snippet of sample code is as follows:
class Person
{
[CsvColumn(Name = "Name", FieldIndex = 1)]
public string UserName { get; set; }
[CsvColumn(Name = "Address 1", FieldIndex = 2)]
public string Address1 { get; set; }
[CsvColumn(Name = "Telephone", FieldIndex = 3)]
public string Telephone { get; set; }
}
private void OutputToCSV(string filenamePrefix, List<Person> people)
{
CsvFileDescription outputFileDescription = new CsvFileDescription
{
SeparatorChar = ','
FirstLineHasColumnNames = true,
FileCultureName = "en-GB"
};
CsvContext cc = new CsvContext();
cc.Write(
people,
#"C:\temp\people.csv",
outputFileDescription);
}
The issue I have is with the telephone number. If it is in the object as 0123456789012 then when I open the CSV in Excel it is seen as a number and the leading zero is removed. I'd like to pre format the column in the CSV as text.
According to Stop Excel from automatically converting certain text values to dates I can start the field with an equals and put the value in quotes, but is there an attribute I can set which will mean that LinqToCSV will do this for me? I don't really want to use LinqToCSV, then open the file and edit it to get it how I want.
Is your intent to create a CSV or an Excel file? If the latter, then wouldn't it be much easier if you have instead used Epplus from Nuget? ie:
void Main()
{
ExcelPackage pck = new ExcelPackage();
List<Person> people = new List<Person> {
new Person { UserName="Cetin", Address1="A1", Telephone="012345"},
new Person { UserName="Timbo", Address1="A2", Telephone="023456"},
new Person { UserName="StackO", Address1="A3", Telephone="0 345 6789 01 23"},
};
var wsEnum = pck.Workbook.Worksheets.Add("MyPeople");
//Load the collection starting from cell A1...
wsEnum.Cells["A1"].LoadFromCollection(people, true, TableStyles.Medium9);
wsEnum.Cells[wsEnum.Dimension.Address].AutoFitColumns();
//...and save
var fi = new FileInfo(#"d:\temp\People.xlsx");
if (fi.Exists)
{
fi.Delete();
}
pck.SaveAs(fi);
}
class Person
{
public string UserName { get; set; }
public string Address1 { get; set; }
public string Telephone { get; set; }
}
Try using OutputFormat to force it ToString(). You may also be able to combine it with your other fix and use OutputFormat="=C" but I have not tried that.
[CsvColumn(Name = "Telephone", FieldIndex = 3, OutputFormat = "C")]
public string Telephone { get; set; }
As you say, this is an Excel issue and you need to the data to have a leading = sign, so that 0123456789012 becomes ="0123456789012".
You can use the OutputFormat to change the format, but you would need to play around with the formatting:
[CsvColumn(FieldIndex = 2, OutputFormat = "dd MMM HH:mm:ss")]

Combine contents of two files using LINQ to CSV

This is probably a dumb question, but I'm wondering how I would fill a list with the following data for a CSV file.
Here's the code so far,
class Info
{
[CsvColumn(Name = "Lease Name", FieldIndex = 1)]
public string leaseName2 { get; set; }
[CsvColumn(Name = "Field Name", FieldIndex = 2)]
public string fieldName2 { get; set; }
[CsvColumn(Name = "Reservoir", FieldIndex = 3)]
public string reservoir2 { get; set; }
[CsvColumn(Name = "Operator", FieldIndex = 4)]
public string operator2 { get; set; }
[CsvColumn(Name = "County", FieldIndex = 5)]
public string county2 { get; set; }
[CsvColumn(Name = "State", FieldIndex = 6)]
public string state2 { get; set; }
[CsvColumn(Name = "Majo", FieldIndex = 7)]
public string majo2 { get; set; }
[CsvColumn(Name = "Resv Cat", FieldIndex = 8)]
public string resvCat2 { get; set; }
[CsvColumn(Name = "Discount Rate", FieldIndex = 9)]
public double disRate2 { get; set; }
There are more columns I just did not want to list them all because that would be redundant. If anyone could help that would be greatly appreciated.
#GertArnold is right, you can use Union(). I used Concat() in my example. Union returns distinct records, Concat doesn't.
using System;
using System.Collections.Generic;
using System.Linq;
using LINQtoCSV;
namespace LinqCsvSandbox
{
class SampleData
{
[CsvColumn(Name = "ID", FieldIndex = 1)]
public string ID { get; set; }
[CsvColumn(Name = "PersonName", FieldIndex = 2)]
public string Name { get; set; }
public override string ToString()
{
return string.Format("{0}: {1}", ID, Name);
}
}
class Program
{
static void Main(string[] args)
{
var inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = false,
FileCultureName = "en-us",
EnforceCsvColumnAttribute = true,
};
CsvContext cc = new CsvContext();
IEnumerable<SampleData> data1 = cc.Read<SampleData>("File1.csv", inputFileDescription);
IEnumerable<SampleData> data2 = cc.Read<SampleData>("File2.csv", inputFileDescription);
IEnumerable<SampleData> all = data1.Concat(data2);
// Uncomment to see the items printed
//foreach(var item in all)
// Console.WriteLine(item);
cc.Write(all, "All.csv");
}
}
}
In my example, File1.csv contains
1,Fred
2,Wilma
File2.csv contains
3,Tango
4,Cash
And the resulting All.csv contains
ID,PersonName
1,Fred
2,Wilma
3,Tango
4,Cash
For those unfamiliar, Linq to CSV is available as a package from NuGet.
Here are a couple of ways, the first is just using an object initializer, the second transfers a small list to the your CSV list:
List<Info> infoList = new List<Info>
{
new Info()
{
leaseName2 = "x"
}
};
List<string> stringList = new List<string> {"xy", "zz"};
infoList.AddRange(stringList.Select(stringVal => new Info()
{
leaseName2 = stringVal
}));

Categories

Resources