I have some Excel file reading code that uses the OLEDB (Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=Excel 8.0;) which works well but I keep encountering an issue whereby certain dates are returned as DBNull.
In the original XLS document, the format of dates that work (en-GB locale) are:
"02/04/2009 17:00:00" // returned as a System.DateTime
And the following style fails:
"08/Jan/09 11:24 AM" // returned as DBNull
Excel knows they're both dates (although I can't force them to style correctly) as the following correctly shows a date:
=DATE(YEAR(c),MONTH(c),DAY(c)) // where c = cell reference.
Is there a way, without altering the auto-generated original, to get the data?
EDIT for reference, here is my read-data method (assuming a dbAdapter is set up already -- note the DBNull doesn't come from the catch which isn't fired at all):
public List<List<string>> GetData(string tableName, int maxColumns)
{
List<List<string>> rows = new List<List<string>>();
DataSet ExcelDataSet = new DataSet();
dbCommand.CommandText = #"SELECT * FROM [" + tableName + "]";
dbAdapter.Fill(ExcelDataSet);
DataTable table = ExcelDataSet.Tables[0];
foreach (DataRow row in table.Rows)
{
List<string> data = new List<string>();
for (int column = 0; column < maxColumns; column++)
{
try
{
data.Add(row[column].ToString());
}
catch (Exception)
{
data.Add(null);
}
}
// Stop processing at first blank row
if ( string.IsNullOrEmpty(data[0]) ) break;
rows.Add(data);
}
return rows;
}
I don't know if this will be helpful or not, but I have run into issues with Excel OLEDB code returning NULLs where I expected data and it almost always came back to a data type inference issue. Excel determines the datatype of a column based on the first x rows of data (I think x=10, could be wrong). I know you don't want to alter the file, but it might be worth trying to put the problem date style in the first 10 rows and see if it alters the behavior of your application.
Obviously if it does fix it, then that doesn't solve your problem. The only fixes in that case that I know of are to alter the file (put something in the first 10 rows that forces it to use the correct datatype). Sorry I can't offer a better solution, but hopefully at least I am helping you figure out what's causing your issue.
Related
I'm trying to read data from an Excel File into a DataSet, but my problem is the DataTable Column Names aren't matching what's in my Excel. It seems to be only Columns with long Column Names. In the DataTable Column Names it cuts off at some point.
Here's my attempt:
DataSet mainExcelDataToImport = new DataSet();
using (OleDbConnection mainExcelOleDbConnection = new OleDbConnection())
{
string theMainExcelConnectionString = this.ExcelConnectionString.Replace("{FullFilePath}", this.SelectedFilePath);
mainExcelOleDbConnection.ConnectionString = theMainExcelConnectionString;
// Open
mainExcelOleDbConnection.Open();
string mainExcelSQL = "SELECT * FROM [{ExcelSheet}$]";
mainExcelSQL = mainExcelSQL.Replace("{ExcelSheet}", selectedExcelSheet);
using (OleDbCommand mainExcelOleDbCommand = mainExcelOleDbConnection.CreateCommand())
{
mainExcelOleDbCommand.CommandText = mainExcelSQL;
mainExcelOleDbCommand.CommandType = CommandType.Text;
// Prepare
mainExcelOleDbCommand.Prepare();
using (OleDbDataAdapter mainOleDbDataAdapter = new OleDbDataAdapter(mainExcelOleDbCommand))
{
mainOleDbDataAdapter.Fill(mainExcelDataToImport);
// Close
mainExcelOleDbConnection.Close();
}
}
}
Here's a .xlsx I've tried:
A1234567890123456789012345678901234567890123456789012345678901234567890
TEST
'A12345.....' is the Column Name at A1
'TEST' is the Value at A2
When I check the `ColumnName` in the `DataTable` I get: 'A123456789012345678901234567890123456789012345678901234567890123' which is 64 characters.
How does the ColumnName MaxLength limitation work?
Can I get rid of it?
Is it maybe a limitation on the OleDbDataAdapter?
Any help / suggestions would be appreciated.
64 characters is unusually long for the name of a database column. I've never seen a column name come anywhere near that length.
It seems odd that you would have a column name so long.
They have to be stored somewhere of course and so there is likely to be a maximum on any database software eg SQL server has a maximum of 128. It would not surprise me if intermediate software like sqlclient had a lower limit. Very long names are so unusual.
It's probably a limitation of the adapter.
You could try some of the alternatives mentioned in this thread:
Best /Fastest way to read an Excel Sheet into a DataTable?
Or maybe you can just work with xml - and not a datatable.
Xml will be able to handle any size field that excel can.
https://learn.microsoft.com/en-us/office/open-xml/understanding-the-open-xml-file-formats
https://learn.microsoft.com/en-us/office/open-xml/how-to-parse-and-read-a-large-spreadsheet
I
I have an ODBC connection using a DSN and I am trying to get the tables and columns from it (for manipulation later) but it is failing at an early stage. The database is a FairCom C-tree (v 6.11) if it matters:
OdbcConnection odbcConn = new OdbcConnection("DSN=Ctree;");
odbcConn.Open();
DataTable tblTables = odbcConn.GetSchema("Tables");
foreach (DataRow row in tblTables.Rows) //displays them one at a time (works)
MessageBox.Show(row["TABLE_NAME"].ToString());
DataTable tblColumns = odbcConn.GetSchema("Columns"); // (why is this empty?)
foreach (DataRow row in tblColumns.Rows) //lists nothing
MessageBox.Show(row["COLUMN_NAME"].ToString() + " : " + row["TABLE_NAME"].ToString());
The issue is that getSchema("Tables") works just fine so I know that the connection works, the drivers are in good shape and that data exists. So why is getSchema("Columns") failing? I also know there is data (hundreds of columns and thousands of rows) inside the db. I am at a loss.
Thoughts?
Unsure if this works with Faircom C-Tree SQL, but try getting the column schema for a single table.
GetSchema() permits the specification of restrictions.
var restrictions = new string[] { null, null, "TableName" };
odbcConn.GetSchema("Columns", restrictions);
You may have to specify the schema name as well (e.g. Sales.SalesPeople)
var restrictions = new string[] { null, "Sales", "SalesPeople" };
odbcConn.GetSchema("Columns", restrictions);
I'm trying to read values from a DataSet in C#/ASP.NET returned from a stored procedure. Normally, the DefaultView from that DataSet is passed into a GridView in an ASP.NET page. in that event, a particular column I'm interested in has a value. However, if I try to read a DataRow and get the column value, it comes through as empty.
For example, this will display a value:
DataSet ds = //////
DataView dv = ds.Tables[0].DefaultView;
grdQuotes.DataSource = dv;
grdQuotes.DataBind();
This, however, gives me no value:
DataSet ds = //////
foreach (DataRow row in ds.Tables[0].Rows) {
String value1 = (String)row["DOReviewDate"];
String value2 = ((Object)row["DOReviewDate"]).ToString();
String value3 = row.Field<String >("DOReviewDate");
}
All three variables end up empty.
I'm pretty lost on where to go with this, as it's apparent that there's a value being pulled from the SQL database, otherwise it wouldn't display in the GridView table on the page. Also, I can get the rest of the column values in the row without problem. Interestingly enough, there is one other column exhibiting the same behavior.
-- EDIT --
Attempt to iterate through rows and columns to get data:
StringBuilder sb = new StringBuilder();
foreach (DataRow row in ds.Tables[0].Rows)
{
StringBuilder r = new StringBuilder();
foreach (DataColumn c in ds.Tables[0].Columns)
{
r.Append(String.Format("{0} | ", row[c]));
}
r.Append("END");
sb.AppendLine(r.ToString());
}
I was finally able to locate the stored procedure powering this functionality. It's returning empty values for the columns I need. There's nothing but an empty string designating what to return. So, while I can't seem to find the functionality within the page construction, it looks like there is some sort of join or alternate search pulling the data, and I believe I found the table to pull from. Guess I'll just have to take this initial data returned to me and use it to pull from the other table. Or, I guess, maybe make my own SQL search with a join. Either way, the stored procedure does not itself contain a join to the table I need. This is what was in the stored procedure:
'' as DOReviewDate
I do not see a way to insert a row in an existing excel file using EPPlus. I am aware of the InsertRow function but this does not insert the row at the end - similar to the insert statement in sql. If this is not possible, how would I find the last used row in an excel file.
Use following code
worksheet.Dimension.End.Row
This should give last used row info.
Here is a method that finds the last cell in a table in an Excel worksheet using EPPlus.
private ExcelRange GetLastContiguousCell(ExcelRange beginCell)
{
var worksheet = beginCell.Worksheet;
var beginCellAddress = new ExcelCellAddress(beginCell.Start.Row, beginCell.Start.Column);
var lastCellAddress = worksheet.Dimension.End;
var bottomCell = worksheet.Cells[beginCellAddress.Row, beginCellAddress.Column, lastCellAddress.Row, beginCellAddress.Column]
.First(cell => cell.Offset(1, 0).Value == null);
var rightCell = worksheet.Cells[beginCellAddress.Row, beginCellAddress.Column, beginCellAddress.Row, lastCellAddress.Column]
.First(cell => cell.Offset(0, 1).Value == null);
return worksheet.Cells[bottomCell.Start.Row, rightCell.Start.Column];
}
An important note, however, is that this assumes there are no gaps in the first row and first column. I use this method for situations where the first row is for column headings (which can't be null) and the first column is a primary Id column (which also can't be null). If your situation differs from this, you will have to adapt the method, but hopefully it will still help.
Edit
It just occurred to me that you might just be able to use worksheet.Dimension.End without all the other code. I use this more complicated method because I sometimes put other information besides the table in my worksheet, and I don't want that to be included in the calculation.
I am retrieving data from an MSSQL server using the SqlDataAdapter and DataSet. From that DataSet I am creating a DataTable. My goal is to convert each column of the table into a string where the elements are comma delimited. I figured that I would try the string conversion first before making the delimiter work.
The code runs in the code-behind of an ASP.Net page. The ultimate goal is to pass the string to a jscript variable, it's a "functional requirement" that I create a delimited string from the columns and that it has to end up as a jscript variable.
Here's what I have thus far:
DataSet myDataSet = new DataSet();
mySqlDataAdapter.Fill(myDataSet);
DataTable temperature = myDataSet.Tables["Table"];
// LOOP1
foreach (DataRow row in temperature.Rows)
// this loop works fine and outputs all elements
// of the table to the web page, this is just to
// test things out
{
foreach (DataColumn col in temperature.Columns)
{
Response.Write(row[col] + " ### ");
}
Response.Write("<br>");
}
// LOOP2
foreach (DataColumn column in temperature.Columns)
// this loop was meant to take all elements for each
// column and create a string, then output that string
{
Response.Write(column.ToString() + "<br>");
}
In LOOP1 things work fine. My data has 4 columns, all are appropriately rendered with one record per row on the web page.
I saw the code for LOOP2 at http://msdn.microsoft.com/en-us/library/system.data.datacolumn.tostring.aspx which seems to do exactly what I need except it does not actually do what I want.
The only thing LOOP2 does is write 4 lines to the web page. Each line has the header of the respective table column but none of the additional data. Clearly there's either a logic flaw on my part or I misunderstand how DataColumn and .toString for it works. Please help me out on this one. Thanks in advance.
EDIT:
Here's an SQL query result example, this is what the Table looks like:
Table quesry result # ImageShack
What I want to end up are four strings, here's an example for the string that would be created from the second column: "-6.7, -7, -7.2, -7.3, -7.3".
This code will concatenate values from cells under each column with ", ":
foreach (var column in temperature.Columns)
{
DataColumn dc = column as DataColumn;
string s = string.Join(", ", temperature.Rows.OfType<DataRow>()
.Select(r => r[dc]));
// do whatever you need with s now
}
For example, for DataTable defined as:
DataTable table = new DataTable();
table.Columns.Add(new DataColumn("Column #1"));
table.Columns.Add(new DataColumn("Column #2"));
table.Rows.Add(1, 2);
table.Rows.Add(11, 22);
table.Rows.Add(111, 222);
... it will produce "1, 11, 111" and "2, 22, 222" strings.
Edit: I saw you chose to declare column as var as opposed to DataColumn, is that a matter of personal preference/style or is there an issue with coding?
Consider following scenario (on the same data table example as above):
// we decide we'll use results later, storing them temporarily here
List<IEnumerable<string>> columnsValues = new List<IEnumerable<string>>();
foreach (DataColumn column in temperature.Columns)
{
var values = temperature.Rows.OfType<DataRow>()
.Select(r => r[column].ToString())
columnsValues.Add(values);
}
We assume we now got list of list of column values. So, when we print them, like this:
foreach (var lisOfValues in columnsValues)
{
foreach (var value in listOfValues)
{
Debug.Write(value + " ");
}
Debug.WriteLine("");
}
We expect to see 1 11 111 followed by 2 22 222. Right?
Wrong.
This code will output 2 22 222 twice. Why? Our .Select(r => r[column].ToString()) captures column variable - not its value, but variable itself - and since we don't use it immediately, once we're out of loop all we know is last value of column.
To learn more about this concept search for closures and captured variables - for example, in posts like this.
Summary:
In this very case you can go with DataColumn in foreach statement. It doesn't matter here because we're enumerating through our .Select(r => r[dc]) either way inside the loop (precisely, string.Join does that for us), producing results before we get to next iteration - whatever we capture, is used immediately.
The link you have posted clearly states
The Expression value, if the property
is set; otherwise, the ColumnName
property.
and that is what is happening. You get column names.
This could help: How to convert a DataTable to a string in C#?