Failed to read correct date from excel (oledb) - c#

We are trying to automate some of the processes that our done daily, for this we have made some Excel files which store the instruction for these processes. In a few such excel files we are using =TODAY() function to populate the cell with today's date.
Now while trying to read these excel files, I am not able to get the correct date in the datatable if excel is not opened at least once on a particular date. The date picked is from the last time the excel file was opened. This is causing a major problem while automating the problem at hand.
I would want to know if this can be achieved using OLEDB drivers and if not what other options are there?
This is the code for reading from the excel, the datasource is constructed at runtime.
private string excelObject = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=\"Excel 8.0;HDR=YES\"";
public DataTable GetSchema()
{
DataTable dtSchema = null;
if (this.Connection.State != ConnectionState.Open) this.Connection.Open();
dtSchema = this.Connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });
return dtSchema;
}
public DataTable ReadTable(string tableName, string criteria)
{
try
{
DataTable resultTable = null;
if (this.Connection.State != ConnectionState.Open)
{
this.Connection.Open();
onReadProgress(10);
}
string cmdText = "Select * from [{0}]";
if (!string.IsNullOrEmpty(criteria))
{
cmdText += " Where " + criteria;
}
OleDbCommand cmd = new OleDbCommand(string.Format(cmdText, tableName));
cmd.Connection = this.Connection;
OleDbDataAdapter adpt = new OleDbDataAdapter(cmd);
onReadProgress(30);
DataSet ds = new DataSet();
onReadProgress(50);
adpt.Fill(ds, tableName);
onReadProgress(100);
if (ds.Tables.Count == 1)
{
return ds.Tables[0];
}
else
{
return null;
}
}
catch
{
MessageBox.Show("Table Cannot be read");
return null;
}
}

Related

External table not in expected format

I know this question have been asked couple of times here but I followed the suggestions and still getting an error : External table is not in expected format.
My application has DataGrid that displays properly the excel file content and when I refresh the DataGrid it shows added row in excel file.But when I open the excel file to check if the row is added ,it's not-no new row is saved.
I'd appreciate your help.
My Wpf application has a connection string in App.config:
add name="ExcelConnectionString" connectionString="Provider=Microsoft.ACE.OLEDB.12.0;Data Source=excel_file\\events2.xlsx;Extended Properties="Excel 12.0 Xml;HDR=YES""
I'm using OLEDB to connect with excel file:
public Events()
{
InitializeComponent();
System.Data.OleDb.OleDbConnection excelConnection;
System.Data.OleDb.OleDbCommand myCommand = new System.Data.OleDb.OleDbCommand();
var connectionString = ConfigurationManager.ConnectionStrings["ExcelConnectionString"].ToString();
excelConnection = new System.Data.OleDb.OleDbConnection(connectionString);
try
{
String sql = null;
excelConnection.Open();
myCommand.Connection = excelConnection;
sql = "Select * from [events$]";
myCommand.CommandText = sql;
myCommand.ExecuteNonQuery();
System.Data.OleDb.OleDbDataAdapter dataAdp = new System.Data.OleDb.OleDbDataAdapter(myCommand);
DataTable dt = new DataTable("events");
dataAdp.Fill(dt);
dgData.ItemsSource = dt.DefaultView;
dataAdp.Update(dt);
excelConnection.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
finally
{
excelConnection.Close();
}
}

How do you programmatically check if a spreadsheet has headers in C#

I am creating a winform application where every day, a user will select a xlsx file with the day's shipping information to be merged with our invoicing data.
The challenge I am having is when the user does not download the xlsx file with the specification that the winform data requires. (I wish I could eliminate this step with an API connection but sadly I cannot)
My first step is checking to see if the xlsx file has headers to that my file path is valid
Example
string connString = "provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + *path* + "';Extended Properties='Excel 12.0;HDR=YES;';";
Where path is returned from an OpenFileDialog box
If the file was chosen wasn't downloaded with headers the statement above throws an exception.
If change HDR=YES; to HDR=NO; then I have trouble identifying the columns I need and if the User bothered to include the correct ones.
My code then tries to load the data into a DataTable
private void loadRows()
{
for (int i = 0; i < deliveryTable.Rows.Count; i++)
{
DataRow dr = deliveryTable.Rows[i];
int deliveryId = 0;
bool result = int.TryParse(dr[0].ToString(), out deliveryId);
if (deliveryId > 1 && !Deliveries.ContainsKey(deliveryId))
{
var delivery = new Delivery(deliveryId)
{
SalesOrg = Convert.ToInt32(dr[8]),
SoldTo = Convert.ToInt32(dr[9]),
SoldName = dr[10].ToString(),
ShipTo = Convert.ToInt32(dr[11]),
ShipName = dr[12].ToString(),
};
Which all works only if the columns are in the right place.
If they are not in the right place my thought is to display a message to the user to get the right information
Does anyone have any suggestions?
(Sorry, first time posting a question and still learning to think through it)
I guess you're loading the spreadsheet into a Datatable? Hard to tell with one line of code. I would use the columns collection in the datatable and check to see if all the columns you want are there. Sample code to enumerate the columns below.
private void PrintValues(DataTable table)
{
foreach(DataRow row in table.Rows)
{
foreach(DataColumn column in table.Columns)
{
Console.WriteLine(row[column]);
}
}
}
private void GetExcelSheetForUpload(string PathName, string UploadExcelName)
{
string excelFile = "DateExcel/" + PathName;
OleDbConnection objConn = null;
System.Data.DataTable dt = null;
try
{
DataSet dss = new DataSet();
String connString = "Provider=Microsoft.ACE.OLEDB.12.0;Persist Security Info=True;Extended Properties=Excel 12.0 Xml;Data Source=" + PathName;
objConn = new OleDbConnection(connString);
objConn.Open();
dt = objConn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
if (dt == null)
{
return;
}
String[] excelSheets = new String[dt.Rows.Count];
int i = 0;
foreach (DataRow row in dt.Rows)
{
if (i == 0)
{
excelSheets[i] = row["TABLE_NAME"].ToString();
OleDbCommand cmd = new OleDbCommand("SELECT * FROM [" + excelSheets[i] + "]", objConn);
OleDbDataAdapter oleda = new OleDbDataAdapter();
oleda.SelectCommand = cmd;
oleda.Fill(dss, "TABLE");
}
i++;
}
grdExcel.DataSource = dss.Tables[0].DefaultView;
grdExcel.DataBind();
lblTotalRec.InnerText = Convert.ToString(grdExcel.Rows.Count);
}
catch (Exception ex)
{
ViewState["Fuletypeidlist"] = "0";
grdExcel.DataSource = null;
grdExcel.DataBind();
}
finally
{
if (objConn != null)
{
objConn.Close();
objConn.Dispose();
}
if (dt != null)
{
dt.Dispose();
}
}
}
if (grdExcel.HeaderRow.Cells[0].Text.ToString() == "CODE")
{
GetExcelSheetForEmpl(PathName);
}
else
{
divStatusMsg.Style.Add("display", "");
divStatusMsg.Attributes.Add("class", "alert alert-danger alert-dismissable");
divStatusMsg.InnerText = "ERROR !!... Upload Excel Sheet in header Defined Format ";
}

Why does OleDbDataAdapter leave artifacts when filling a DataTable?

I wrote some methods which are supposed to fetch a DataTable for each WorkSheet in a Excel file:
Step 1 is to get the names of all sheets included in a .xlsx file:
private static List<string> GetSheetNames(string filePath)
{
List<string> sheetNames = new List<string>();
DataTable dt = null;
try
{
OleDbConnection connection = new OleDbConnection("provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + filePath + "';Extended Properties='Excel 12.0 Xml;HDR=YES;'");
connection.Open();
dt = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
if (dt == null)
{
return null;
}
// Add the sheet name to the string array.
foreach (DataRow row in dt.Rows)
{
sheetNames.Add(row["TABLE_NAME"].ToString());
}
}catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
return sheetNames;
}
Step 2 is to read every sheet and return an according DataTable:
private static DataTable ReadExcelSheet(string filePath,string sheetName)
{
DataTable table = new DataTable();
ValidateSheetName(ref sheetName);
try
{
OleDbConnection connection;
DataSet DtSet;
OleDbDataAdapter cmd;
connection = new OleDbConnection("provider=Microsoft.ACE.OLEDB.12.0;Data Source='" + filePath + "';Extended Properties='Excel 12.0 Xml;HDR=YES;'");
cmd = new OleDbDataAdapter("select * from ["+sheetName+"]", connection);
cmd.TableMappings.Add("Table", sheetName.Replace("$",string.Empty));
DtSet = new DataSet();
cmd.Fill(DtSet);
table = DtSet.Tables[0];
connection.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
return table;
}
Both methods are called from this last method which returns a List<DataTable>:
private static List<DataTable> ConvertExcelToTables(string filePath)
{
List<string> sheetNames = GetSheetNames(filePath);
List<DataTable> tableList = new List<DataTable>();
foreach(string sheetName in sheetNames)
{
tableList.Add(ReadExcelSheet(filePath,sheetName));
}
return tableList;
}
There is also a little helper method which should be irrelevant for the question:
private static void ValidateSheetName(ref string sheetName)
{
sheetName = sheetName.EndsWith("$") ? sheetName : sheetName + "$";
}
If I take one sheet from a example file it looks like this:
Now no matter if I just look into the DataTable while debugging or if I bind it as a DataSource of a DataGridView the result looks a little weird:
My guess is that this might have to do with Excel sheets beginning counting with 1 not with 0. But even if this is the case I can't really think of a solution. Or did I miss something. Actually this is a pity because this seems to be a clean solution imo.
No, the problem is caused by
HDR=YES;
in your connection string.
Change it to
HDR=NO;
HDR=YES means that the first line of your Excel sheets is assumed to contain the fields' names of your table. But this is not the case with the sheet shown as an example. Indeed the OleDb provider cannot determine the name of the second column (it's blank) and thus it assigns the default value (the letter F followed by the progressive number of the column)
You could find a lot of examples and explanations about connectionstrings for excel at connectionstrings.com

multi-sheet import with oledb netting "_xlnm#_FilterDatabase" as sheet names

i have an excel with multi-sheets that i want to import
the code is fairly simple and basic and should work
but my sheetnames keep coming back as "_xlnm#_FilterDatabase" in the debugger
and is the root of my prob
here is the relevant portion of the code:
string sheetName = "";
file = HostingEnvironment.MapPath("~/files/master1.xlsx");
xConnStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + file + ";Extended Properties=\"Excel 12.0 Xml;HDR=YES;IMEX=1\";";
using (OleDbConnection connection = new OleDbConnection(xConnStr))
{
// get sheet names
connection.Open();
DataTable sheets = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
// eeo10 (2nd tab: first sheet to be imported)
sheetName = sheets.Rows[1]["TABLE_NAME"].ToString();
OleDbCommand command = new OleDbCommand("Select * FROM ["+sheetName+"]", connection);
// Create DbDataReader to Data Worksheet
using (OleDbDataReader dr = command.ExecuteReader())
{
// Bulk Copy to SQL Server
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConnx))
{
bulkCopy.ColumnMappings.Add("code", "id");
bulkCopy.ColumnMappings.Add("category10", "category");
bulkCopy.DestinationTableName = "eeo10";
bulkCopy.WriteToServer(dr);
}
}
// eeo14 (3rd tab: second sheet to be imported)
sheetName = sheets.Rows[2]["TABLE_NAME"].ToString();
command = new OleDbCommand("Select * FROM [" + sheetName + "]", connection);
DataTable cols = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Columns, new object[] { null, null, sheetName, null });
foreach (DataRow r in cols.Rows)
{
System.Diagnostics.Debug.WriteLine("{0} = {1}", r["COLUMN_NAME"], r["ORDINAL_POSITION"]);
}
// Create DbDataReader to Data Worksheet
using (OleDbDataReader dr = command.ExecuteReader())
{
// Bulk Copy to SQL Server
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConnx))
{
bulkCopy.ColumnMappings.Add("code", "id");
bulkCopy.ColumnMappings.Add("category14", "category");
bulkCopy.DestinationTableName = "eeo14";
bulkCopy.WriteToServer(dr);
}
}
}
so as stated before
the debugger returns sheetName="_xlnm#_FilterDatabase"
which weirdly enough the first one eeo10 works
but eeo14 doesnt because its still trying working with the eeo10 sheet
here maybe some other relevant pieces of information:
- i have turned off autofilter in the workbook
- i put in the column header printout just to confirm which sheet it was reading
any insights would be apprec
thanks so much!
Excel creates a hidden sheet each time you filter on a sheet and all though this sheet should not be available when retrieving the sheet names.
Here is a piece of code that will help you get the sheet names using System.Data.OleDb:
class Retriever
{
public List<SheetName> GetSheetNames(OleDbConnection conn)
{
List<SheetName> sheetNames = new List<SheetName>();
if (conn.State != ConnectionState.Open)
{
conn.Open();
}
DataTable excelSchema = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
foreach (DataRow row in excelSchema.Rows)
{
if (!row["TABLE_NAME"].ToString().Contains("FilterDatabase"))
{
sheetNames.Add(new SheetName() { sheetName = row["TABLE_NAME"].ToString(), sheetType = row["TABLE_TYPE"].ToString(), sheetCatalog = row["TABLE_CATALOG"].ToString(), sheetSchema = row["TABLE_SCHEMA"].ToString() });
}
}
conn.Close();
return sheetNames;
}
}
class SheetName
{
public string sheetName { get; set; }
public string sheetType { get; set; }
public string sheetCatalog { get; set; }
public string sheetSchema { get; set; }
}
Please get back to me if you're having any kind of issues with this.
Have fun!

How can I read the rows of an Excel csv or xls file into an ASP.NET app from a remote location using C#?

Here's my situation. I'm designing a program that takes Excel files (which may be in csv, xls, or xlsx format) from a remote network drive, processes the data, then outputs and stores the results of that process. The program provides a listbox of filenames that are obtained from the remote network drive folder using the method detailed in the accepted answer here. Once the user selects a filename from the listbox, I want the program to find the file and obtain the information from it to do the data processing. I have tried using this method to read the data from the Excel file while in a threaded security context, but that method just fails without giving any kind of error. It seems to not terminate. Am I going about this the wrong way?
Edit - (Final Notes: I have taken out the OleDbDataAdapter and replaced it with EPPlus handling.)
I was able to scrub sensitive data from the code, so here it is:
protected void GetFile(object principalObj)
{
if (principalObj == null)
{
throw new ArgumentNullException("principalObj");
}
IPrincipal principal = (IPrincipal)principalObj;
Thread.CurrentPrincipal = principal;
WindowsIdentity identity = principal.Identity as WindowsIdentity;
WindowsImpersonationContext impersonationContext = null;
if (identity != null)
{
impersonationContext = identity.Impersonate();
}
try
{
string fileName = string.Format("{0}\\" + Files.SelectedValue, #"RemoteDirectoryHere");
string connectionString = string.Format("Provider=Microsoft.ACE.OLEDB.14.0; data source={0}; Extended Properties=Excel 14.0;", fileName);
OleDbDataAdapter adapter = new OleDbDataAdapter("SELECT * FROM Sheet1", connectionString);
DataSet ds = new DataSet();
adapter.Fill(ds, "Sheet1");
dataTable = ds.Tables["Sheet1"];
}
finally
{
if (impersonationContext != null)
{
impersonationContext.Undo();
}
}
}
Additional Edit
Now xlsx files have been added to the mix.
Third Party
Third party solutions are not acceptable in this case (unless they allow unrestricted commercial use).
Attempts - (Final Notes: Ultimately I had to abandon OleDb connections.)
I have tried all of the different connection strings offered, and I have tried them with just one file type at a time. None of the connection strings worked with any of the file types.
Permissions
The User does have access to the file and its directory.
Your connection string might be the issue here. As far as I know, there isn't 1 that can read all xls, csv, and xlsx. I think you're using the XLSX connection string.
When I read xls, i use the following connection string:
#"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + sFilePath + ";Extended Properties='Excel 8.0;HDR=YES;IMEX=1;'"
Having said that, I recommend using a 3rd party file reader/parser to read XLS and CSV since, from my experience, OleDbDataAdapter is wonky depending on the types of data that's being read (and how mixed they are within each column).
For XLS, try NPOI https://code.google.com/p/npoi/
For CSV, try http://www.codeproject.com/Articles/9258/A-Fast-CSV-Reader
For XLSX, try EPPlus http://epplus.codeplex.com/
I've had great success with the above libraries.
Is it really important that you use an OleDb interface for this? I've always done it with Microsoft.Office.Excel.Interop, to wit:
using System;
using Microsoft.Office.Interop.Excel;
namespace StackOverflowExample
{
class Program
{
static void Main(string[] args)
{
var app = new Application();
var wkbk = app.Workbooks.Open(#"c:\data\foo.xls") as Workbook;
var wksht = wkbk.Sheets[1] as Worksheet; // not zero-based!
for (int row = 1; row <= 100; row++) // not zero-based!
{
Console.WriteLine("This is row #" + row.ToString());
for (int col = 1; col <= 100; col++)
{
Console.WriteLine("This is col #" + col.ToString());
var cell = wksht.Cells[row][col] as Range;
if (cell != null)
{
object val = cell.Value;
if (val != null)
{
Console.WriteLine("The value of the cell is " + val.ToString());
}
}
}
}
}
}
}
As you will be dealing with xlsx extension, you should rather opt for the new connection string.
public static string getConnectionString(string fileName, bool HDRValue, bool WriteExcel)
{
string hdrValue = HDRValue ? "YES" : "NO";
string writeExcel = WriteExcel ? string.Empty : "IMEX=1";
return "Provider=Microsoft.ACE.OLEDB.12.0;" + "Data Source=" + fileName + ";" + "Extended Properties=\"Excel 12.0 xml;HDR=" + hdrValue + ";" + writeExcel + "\"";
}
Above is the code for getting the connection string. First argument expects the actual path for file location. Second argument will decide whether to consider first row values as column headers or not. Third argument helps decide whether you want to open the connection to create and write the data or simply read the data. To read the data set it to "FALSE"
public static ReadData(string filePath, string sheetName, List<string> fieldsToRead, int startPoint, int endPoint)
{
DataTable dt = new DataTable();
try
{
string ConnectionString = ProcessFile.getConnectionString(filePath, false, false);
using (OleDbConnection cn = new OleDbConnection(ConnectionString))
{
cn.Open();
DataTable dbSchema = cn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
if (dbSchema == null || dbSchema.Rows.Count < 1)
{
throw new Exception("Error: Could not determine the name of the first worksheet.");
}
StringBuilder sb = new StringBuilder();
sb.Append("SELECT *");
sb.Append(" FROM [" + sheetName + fieldsToRead[0].ToUpper() + startPoint + ":" + fieldsToRead[1].ToUpper() + endPoint + "] ");
OleDbDataAdapter da = new OleDbDataAdapter(sb.ToString(), cn);
dt = new DataTable(sheetName);
da.Fill(dt);
if (dt.Rows.Count > 0)
{
foreach (DataRow row in dt.Rows)
{
string i = row[0].ToString();
}
}
cn.Dispose();
return fileDatas;
}
}
catch (Exception)
{
}
}
This is for reading 2007 Excel into dataset
DataSet ds = new DataSet();
try
{
string myConnStr = "";
myConnStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=MyDataSource;Extended Properties=\"Excel 12.0;HDR=YES\"";
OleDbConnection myConn = new OleDbConnection(myConnStr);
OleDbCommand cmd = new OleDbCommand("select * from [Sheet1$] ", myConn);
OleDbDataAdapter adapter = new OleDbDataAdapter();
adapter.SelectCommand = cmd;
myConn.Open();
adapter.Fill(ds);
myConn.Close();
}
catch
{ }
return ds;

Categories