Reading from Excel File - c#

I have seen many examples of this around but something isn't working for me.
What I am looking to do is to read an Excel sheet, given a sheet and store those values into Lists.
For example, say I have an excel file that looks like:
First Second Third
f1 s1 t1
f2 s2 t2
f3 s3 t3
Each row is to be considered a set of values.
This is what I have doing so far:
List<string> ColumnNames= GetColumnNames();
using (OleDbConnection OleDbConn = new OleDbConnection(Path))
{
OleDbConn.Open();
String cmdString = "SELECT * FROM [" + sheetName+ "]";
OleDbCommand cmd = new OleDbCommand(cmdString, OleDbConn);
DataTable dt = new DataTable();
List<ValueSet> sets = new List<ValueSet>();
Dictionary<string, Value> values = new Dictionary <string,value>()
ValueSet valueset = new ValueSet(null);
using (OleDbDataReader oleRdr = cmd.ExecuteReader())
{
while (oleRdr.Read())
{
for (int i = 0; i < ColumnNames.Count; i++)
{
ColumnName cn = new ColumnName(columnNames[i]);
string data= oleRdr[f.Name].ToString();
Value value = new Value(data, f);
if (!values.ContainsKey(ColumnNames[i]))
{
values.Add(ColumnNames[i], value);
}
else
{
values[ColumnNames[i]] = value;
}
}
valueSet= new ValueSet(values);
sets.Add(valueSet);
}
return sets;;
}

I've gotten weird results with certain files using an OleDbConnection.
I suggest http://www.codeproject.com/Articles/11698/A-Portable-and-Efficient-Generic-Parser-for-Flat-F
With this you can read your CSV into a datatable and parse it into a list as follows:
DataTable dtPrereg;
using (GenericParserAdapter gp = new GenericParserAdapter(Server.MapPath("prereg.csv"), Encoding.UTF8))
{
gp.FirstRowHasHeader = true;
dtPrereg = gp.GetDataTable();
}
I haven't tested this on tab delimited files, but it should work the same (or you could convert your file to CSV)

If you really have a spreadsheet with a known number of named columns and you want to project them into a List<List<string>> it's a lot easier to just do it with Linq.
e.g.
List<List<string>> data;
using (OleDbDataReader rdr = cmd.ExecuteReader())
{
data = (from row in rdr.Cast<DbDataRecord>()
select new List<string>
{
row["First"].ToString(),
row["Second"].ToString(),
row["Third"].ToString()
}).ToList();
}

try changing
ValueSet= new ValueSet(values);
sets.Add(ValueSet);
to
valueset = new ValueSet(values);
sets.Add(valueset );

Related

How do I read SQL row values of a column into an array?

I have created a method that will get data from a SQL table and store the columns of data each in their own array. Right now, when working through the debugger, what I notice is that when I am assigning values to these arrays, they are null values.
I did check to see if my query returns values in SSMS and it indeed does. So null values should not be expected.
Here is the code to the method:
public static CommentsPageData getComments(string wcNum)
{
string[] prodTimeArray = new string[24];
string[] crewsArray = new string[24];
string[] commentsArray = new string[24];
string[] redTimeArray = new string[24];
string[] greenTimeArray = new string[24];
string commandSql = "SELECT TOP 24 Production_Time, Crew, Red_Time, Green_Time, Comment FROM ************ WHERE Work_Center = #wc ORDER BY Production_Time DESC";
SqlConnection con = new SqlConnection("Data Source=*******;Initial Catalog=********;Integrated Security=True");
con.Open();
SqlCommand cmd = new SqlCommand(commandSql, con);
cmd.Parameters.AddWithValue("wc", wcNum);
CommentsPageData commPageData = new CommentsPageData();
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
prodTimeArray.Append(reader["Production_Time"].ToString());
crewsArray.Append(reader["Crew"].ToString());
redTimeArray.Append(reader["Red_Time"].ToString());
greenTimeArray.Append(reader["Green_Time"].ToString());
commentsArray.Append(reader["Comment"].ToString());
}
}
else
{
Console.WriteLine("No rows found");
}
reader.Close();
}
commPageData.ProdTimes = prodTimeArray;
commPageData.Crews = crewsArray;
commPageData.GreenTime = greenTimeArray;
commPageData.RedTime = redTimeArray;
commPageData.Comments = commentsArray;
con.Close();
return commPageData;
}
Long story short, I have created a Class (CommentsPageData) which has an array for each column I'm returning 24 values from. However... The problem is in the while(reader.Read()){} section of the method. I can see it assigning values, but it is just assigning null values.
How can I actually get the values and assign them to my array correctly?
Its just like Jeroen Mostert said, Arrays in C# do not change in size after they are declared. You have declared your arrays inside the class which means the size you have initialized your arrays with is 0 which means no matter how many times you try to append elements to them, the arrays will be null. Use System.Collections.Generic List<Type> to hold your data as it can be updated to hold more elements.
public static CommentsPageData getComments(string wcNum)
{
List<string> prodTimeArray = new List<string>();
List<string> crewsArray = new List<string>();
List<string> commentsArray = new List<string>();
List<string> redTimeArray = new List<string>();
List<string> greenTimeArray = new List<string>();
string commandSql = "SELECT TOP 24 Production_Time, Crew, Red_Time, Green_Time, Comment FROM ************ WHERE Work_Center = #wc ORDER BY Production_Time DESC";
SqlConnection con = new SqlConnection("Data Source=*******;Initial Catalog=********;Integrated Security=True");
con.Open();
SqlCommand cmd = new SqlCommand(commandSql, con);
cmd.Parameters.AddWithValue("wc", wcNum);
CommentsPageData commPageData = new CommentsPageData();
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
prodTimeArray.Add(reader["Production_Time"].ToString());
crewsArray.Add(reader["Crew"].ToString());
redTimeArray.Add(reader["Red_Time"].ToString());
greenTimeArray.Add(reader["Green_Time"].ToString());
commentsArray.Add(reader["Comment"].ToString());
}
}
else
{
Console.WriteLine("No rows found");
}
reader.Close();
}
commPageData.ProdTimes = prodTimeArray.ToArray();
commPageData.Crews = crewsArray.ToArray();
commPageData.GreenTime = greenTimeArray.ToArray();
commPageData.RedTime = redTimeArray.ToArray();
commPageData.Comments = commentsArray.ToArray();
con.Close();
return commPageData;
}
I would use this code
reader = cmd.ExecuteReader()
var dt=new DataTable;
dt.Load(reader);
Dictionary<string, string[]> dict = new();
for (var i = 0; i < dt.Rows.Count; i++)
{
for (var j = 0; j < dt.Rows[i].ItemArray.Length; j++)
{
if (!dict.ContainsKey(dt.Columns[j].ColumnName))
dict.Add(dt.Columns[j].ColumnName, new string[dt.Rows.Count]);
dict[dt.Columns[j].ColumnName][i] = dt.Rows[i].ItemArray[j].ToString();
}
}
and just use the dictionary
but if you want a name for each array
string[] prodTimeArray;
string[] crewsArray;
string[] commentsArray;
....
prodTimeArray = dict["Production_Time"];
crewsArray = dict["Crew"];
commentsArray = dict["Comment"];
....
If you really need to have this end result, I'd recommend just making an object representing a row in the table, use Dapper to return the rows into an IEnumerable of the objects, then to a LINQ select for each column into an array
For Example:
var results = await con.QueryAsync<MyRowObject>(commandSql, new {"wc"}, commandType: Text);
var productionTimeArray = results.Select(x => x.ProductionTime).ToArray();

Create a list within a list having the data readed into MYSQL

I want to create a list within a list having the data readed into MYSQL...
I'm close but it seems that it works not properly...
When I MessageBox.Show(list_line[0][3].ToString());
it gives me System.IndexOutOfRangeException but technically, what i want and think have done is :
list_DB[ [list_line1] , [List_line2] , [list_line3] , ... ]
and list_line looks like [1, 22/02/2017 13:48:01, 50.0000, 004.0000]
Here's my function...
private List<string> list_line = new List<string>();
private List<List<string>> list_DB = new List<List<string>>();
private void BoatGPS_INTO_LIST()
{
SqlDataReader reader;
string query = "select boat_id, boatGPS_DateTime, boatGPS_lat, boatGPS_lon from BoatGPS";
using (SqlConnection sql = new SqlConnection("Data Source = (LocalDB)\\MSSQLLocalDB; AttachDbFilename = \"" + Environment.CurrentDirectory + "\\DB\\STS.mdf\"; Integrated Security = True; Connect Timeout = 30"))
{
sql.Open();
using (reader = new SqlCommand(query, sql).ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
Object[] line = new Object[reader.FieldCount];
reader.GetValues(line);
foreach (var item in line)
{
list_line.Add(item.ToString());
}
list_DB.Add(list_line);
}
}
}
MessageBox.Show(list_line[0][3].ToString());
sql.Close();
}
}
list_DB is of type List<List<string>>. Try:
MessageBox.Show(list_DB[0][3].ToString());
EDIT:
Concerning how to fill the list list_DB, try:
while (reader.Read())
{
List<string> list_line = new List<string>();
list_line.Add(reader[0].ToString());
list_line.Add(reader[1].ToString());
list_line.Add(reader[2].ToString());
list_line.Add(reader[3].ToString());
list_DB.Add(list_line);
}
This code
MessageBox.Show(list_line[0][3].ToString());
is trying to access the fist item (string) in list_line object (List<string>) and then character with index 3 inside that string, which will throw exception if string is shorter than 4 characters.
You are complicating things. No need of such structure.
There is a specific class to represent the in memory the result of your SELECT query and it is the DataTable class.
private DataTable dt = new DataTable();
private void BoatGPS_INTO_LIST()
{
SqlDataReader reader;
string query = "select boat_id, boatGPS_DateTime, boatGPS_lat, boatGPS_lon from BoatGPS";
using (SqlConnection sql = new SqlConnection("Data Source = (LocalDB)\\MSSQLLocalDB; AttachDbFilename = \"" + Environment.CurrentDirectory + "\\DB\\STS.mdf\"; Integrated Security = True; Connect Timeout = 30"))
{
sql.Open();
using (reader = new SqlCommand(query, sql).ExecuteReader())
dt.Load(reader);
if(dt.Rows.Count > 0)
// Here the syntax means: First row, fourth column
MessageBox.Show(dt.Rows[0][3].ToString());
else
MessageBox.Show("Table is empty");
}
}

Incorrect error data from excel

I am importing data from excel to mySql DB using OLEDB provider. When there is a field with %, it automatically get divided by 100. Let say I have 2 column Qty and Disc_Percentage with value 4 and 25%. When I got data from excel sheet to datatable, it convert Disc_Percentage value to 0.25. I have below code for reading excel
public static DataTable ConvertExcelFileDataToDataTable(string file, string extension)
{
var dtImportedData = new DataTable();
// -- Start of Constructing OLEDB connection string to Excel file
var props = new Dictionary<string, string>();
// For Excel 2007/2010
if (file.ToLower().EndsWith(".xlsx"))
{
props["Provider"] = "Microsoft.ACE.OLEDB.12.0;";
props["Extended Properties"] = "\"Excel 12.0\"";
}
// For Excel 2003 and older
else if (file.ToLower().EndsWith(".xls"))
{
props["Provider"] = "Microsoft.ACE.OLEDB.12.0;";
props["Extended Properties"] = "\"Excel 8.0;IMEX=1;HDR=Yes\"";
}
else
return null;
props["Data Source"] = file;
var sb = new StringBuilder();
foreach (KeyValuePair<string, string> prop in props)
{
sb.Append(prop.Key);
sb.Append('=');
sb.Append(prop.Value);
sb.Append(';');
}
//You must use the $ after the object you reference in the spreadsheet
var conn = new OleDbConnection(sb.ToString());
conn.Open();
var myCommand = new OleDbDataAdapter();
DataTable dtSerialNumbers = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
if (dtSerialNumbers == null)
{
return dtImportedData;
}
var excelSheets = new String[dtSerialNumbers.Rows.Count];
// Add the sheet name to the string array.
if (dtSerialNumbers.Rows.Count > 0)
{
//we need the first sheet so save the first sheet name from the first row of the table
excelSheets[0] = dtSerialNumbers.Rows[0]["TABLE_NAME"].ToString();
myCommand = new OleDbDataAdapter("SELECT * FROM [" + excelSheets[0] + "]", conn);
}
myCommand.Fill(dtImportedData);
dtImportedData.TableName = excelSheets[0].Replace("$", String.Empty);
return dtImportedData;
}
Excel(2016 on my machine) autodetects entered '%' and sets the cell-format to percent. I suggest reformating those cells before importing.

Get distinct values from column by column

Have to get each columns distinct data and store to the Dictionary (or array) using Excel.interop. I have tried the following code, but it does not align with Excel.interop.
var excel = new ExcelQueryFactory("worksheetFileName");
var distinctNames = (from row in excel.WorkSheet() select row["ColB"]).Distinct();
Please provide the Excel.Interop snippet/code to get distinct values column by column and store in array.
For this operation it does not make sense to using Excel automation, instead the prudent course of action is to work with OleDb unless there is a sound reason for using Excel automation.
Example, figure 1 is a function to create a connection string which can be used in any project while figure 2 is for reading data.
To work with Excel automation we open ourselves up to objects not being disposed of if there is a crash or that you do not code properly (this I call the two dot rule) when objects can't be released because of how you created and used automation objects which does not happen with OleDb. Now if you wanted formatting than we move to automation.
public string ConnectionString(string FileName, string Header)
{
OleDbConnectionStringBuilder Builder = new OleDbConnectionStringBuilder();
if (System.IO.Path.GetExtension(FileName).ToUpper() == ".XLS")
{
Builder.Provider = "Microsoft.Jet.OLEDB.4.0";
Builder.Add("Extended Properties", string.Format("Excel 8.0;IMEX=1;HDR={0};", Header));
}
else
{
Builder.Provider = "Microsoft.ACE.OLEDB.12.0";
Builder.Add("Extended Properties", string.Format("Excel 12.0;IMEX=1;HDR={0};", Header));
}
Builder.DataSource = FileName;
return Builder.ConnectionString;
}
Code to read the first column in Sheet2 and get distinct values, in this case I am working against a column with dates as string into List where the file resides in the same folder as the app executable
private List<string> DemoDistinct()
{
List<string> dateList = new List<string>();
DataTable dt = new DataTable();
using (OleDbConnection cn = new OleDbConnection { ConnectionString = ConnectionString(System.IO.Path.Combine(Application.StartupPath, "WS1.xlsx"), "Yes") })
{
cn.Open();
using (OleDbCommand cmd = new OleDbCommand
{
CommandText = "SELECT DISTINCT [Dates] FROM [Sheet2$]",
Connection = cn
}
)
{
OleDbDataReader dr = cmd.ExecuteReader();
dt.Load(dr);
dateList = dt
.AsEnumerable()
.Select(row => row.Field<DateTime>("Dates").ToShortDateString()).ToList();
}
}
return dateList;
}

how to get tables of an access db into a list box using c#?

i needed to create a form in which i hav to browse and open mdb files ---> i did this part usin oprnfile dialogue!
private void button1_Click(object sender, EventArgs e)
{
OpenFileDialog oDlg = new OpenFileDialog();
oDlg.Title = "Select MDB";
oDlg.Filter = "MDB (*.Mdb)|*.mdb";
oDlg.RestoreDirectory = true;
string dir = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
oDlg.InitialDirectory = dir;
DialogResult result = oDlg.ShowDialog();
if (result == DialogResult.OK)
{
textBox1.Text = oDlg.FileName.ToString();
}
}
**this is my code so far!!!
now i need to make 3 list boxes!!
1st one to display the table names of the db!
2nd to to display field names when clicked on table name!!!
3rd to display attributes on fiels on clickin on it!
v can edit the attribute values and on clickin of save button it should update the database!!!
This class should get you the information you need.
public static class DatabaseInfoCollector
{
public static System.Collections.Generic.List<string> GetTables(string file)
{
System.Data.DataTable tables;
using(System.Data.OleDb.OleDbConnection connection = new System.Data.OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + file))
{
connection.Open();
tables = connection.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Tables,new object[]{null,null,null,"TABLE"});
}
System.Collections.Generic.List<string> Tables = new System.Collections.Generic.List<string>();
for (int i = 0; i < tables.Rows.Count; i++)
{
Tables.Add(tables.Rows[i][2].ToString());
}
return Tables;
}
public static System.Collections.Generic.List<string> GetColumnNames(string file, string table)
{
System.Data.DataTable dataSet = new System.Data.DataTable();
using(System.Data.OleDb.OleDbConnection connection = new System.Data.OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + file))
{
connection.Open();
System.Data.OleDb.OleDbCommand Command = new System.Data.OleDb.OleDbCommand("SELECT * FROM " + table,connection);
using(System.Data.OleDb.OleDbDataAdapter dataAdapter = new System.Data.OleDb.OleDbDataAdapter(Command))
{
dataAdapter.Fill(dataSet);
}
}
System.Collections.Generic.List<string> columns = new System.Collections.Generic.List<string>();
for(int i = 0; i < dataSet.Columns.Count; i ++)
{
columns.Add(dataSet.Columns[i].ColumnName);
}
return columns;
}
}
Fill the tables list like this.
System.Collections.Generic.List<string> Tables = DatabaseInfoCollector.GetTables(textBox1.Text);
foreach(string table in Tables)
{
cboTable.Items.Add(table);
}
Fill the columns like this.
System.Collections.Generic.List<string> Columns = DatabaseInfoCollector.GetColumnNames(textBox1.Text,cboTable.SelectedItem.ToString());
foreach(string column in Columns)
{
cboColumns.Items.Add(column);
}
You can also use this method to return a DataTable containing all kinds of information about each column.
public static System.Data.DataTable GetSchemaData(string file)
{
System.Data.DataTable columns;
using(System.Data.OleDb.OleDbConnection connection = new System.Data.OleDb.OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + file))
{
connection.Open();
columns = connection.GetOleDbSchemaTable(System.Data.OleDb.OleDbSchemaGuid.Columns,new object[]{null,null,null,null});
}
return columns;
}
Use the System.Data.OleDb.* classes to get the data from the access file.
Example:
//Create the OleDbConnection object
//and associate it with our database
using(OleDbConnection conn = new OleDbConnection(
"PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source="+textBox1.Text)){
//Open the database connection
conn.Open();
//Create an OleDbCommand object and
//pass it the SQL read query and the connection to use
OleDbCommand cmd = new OleDbCommand(sqlstr,conn);
//Procure the OleDbDataReader object to browse the recordset
OleDbDataReader rdr = cmd.ExecuteReader();
//Keep reading records in the forward direction
while (rdr.Read())
{
//Use one of the various methods available to read the data
//Eg:- GetValue, GetValues, Item etc.
. . .
. . .
}
}
Use System.Data.OleDb to open a connection with new OleDbConnection(connectionString).
The connection string should be "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=[PathToMDBFile]"
var conn = new OleDbConnection(connectionString);
var ds = new DataSet();
var adapter = new OleDbDataAdapter("SELECT Column1 FROM Table1", conn);
conn.Open();
adapter.Fill(ds);
conn.Close();
var value = ds.Tables[0].Rows[0]["Column1"].ToString();
That will get you the first value in the column named Column1

Categories

Resources