Problem with table names in excel with oledb driver - c#

I am using Oledb driver to load data from excel for displaying in tabcontrol with datargids
I am using following loop to load data from every sheet
foreach (string str in sheets)
{
string query = "SELECT * FROM [" + str + "]";
adapter.SelectCommand.CommandText = query;
adapter.Fill(ds,str);
}
It works well until sheet names start with numbers. I have a excel file whose sheet name is 26203 REV C (EVK). It throws me error as The Microsoft Jet database engine could not find the object ''26203 REV C [EVK]$'_'. Make sure the object exists and that you spell its name and the path name correctly.What can be the remedy to the problem. I do not have any control over the sheet names.

Try using backticks (`) instead of brackets:
SELECT * FROM `26203 REV C (EVK)$`
Remember that you also need to include the dollar symbol suffix when selecting.

Related

C# How to import specific column name of excel into DataTable

I try to import an excel in to my DataTable with condition.
example : - The user have my provided excel import template, with the first row 0 as the column header (ItemCode, QTY, SerialNo, Remarks). But due to the user might accidentally insert few unwanted column name in anywhere of my per-ready column or delete one of my column name.
I try to build a code regardless what happen, the system only detect my standard ready column header (ItemCode, QTY, SerialNo, Remarks). And only will add the column still within the excel and ignore those accidentally delete column name.
What is the best way to code the detection of the column name when is exist before allow to import those specific column into dataTable?
Below is my current excel import code (which i try to add the above condition code)
private DataTable ReadExcelToDataTable(string filePath)
{
tableSalesOrder = new DataTable("dtSO");
string strConn = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0 Xml;HDR=YES;IMEX=1;TypeGuessRows=0;ImportMixedTypes=Text\"", filePath);
using (OleDbConnection dbConnection = new OleDbConnection(strConn))
{
dbConnection.Open();
DataTable dtExcelSchema = dbConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
string sSheetName = dtExcelSchema.Rows[0]["TABLE_NAME"].ToString();
dbConnection.Close();
using (OleDbDataAdapter dbAdapter = new OleDbDataAdapter("SELECT DISTINCT * FROM [" + sSheetName + "]", dbConnection)) //rename sheet if required!
dbAdapter.Fill(tableSalesOrder);
}
return tableSalesOrder;
}
I have try to google around, found many hint but still unable to make it work.
Thank you for any advice.
If you just wanted to ignore extra columns, you could use
... new OleDbDataAdapter("Select Distinct ItemCode, QTY, SerialNo, Remarks FROM [" + sSheetName + "] ...
If you need to cope with some of these columns being missing, then it is slightly more complicated. You need to get a list of columns in the excel sheet , eg
DataTable dt = dbConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Columns,
new object[] { null,null, sSheetName, null });
List<string> columnNames = new List<string>();
foreach (DataRow row in dt.Rows)
columnNames.Add(row["Column_name"].ToString());
Then you need to build up your select statement to only include the columns that you want and that exist in the excel sheet.
You could set up your workbooks with named ranges, and extract those. That way it'll work even if they accidentally change the name or insert extra columns. You can select the named range with something like this:
var sql = "SELECT * FROM [Workbook$MyNamedRange]"
using (OleDbDataAdapter dbAdapter = new OleDbDataAdapter(sql, dbConnection));
dbAdapter.Fill(tableSalesOrder);
I solve the issue by using different method, I got the idea from both your guys advise. In fact, after i do some test on my origin code which posted, it only will import according the column name which i define which is ItemCode, Qty, SerialNo & Remakrs at my gridView which my dataTable have assign to it as data source.
The only issue is when one of the column deleted, my import process will be facing problem. This due to the datatable never assign any column name after it created.
I solve it by improve the dataTable set and redefine the column name into It.
if (tableSalesOrder == null)
{
tableSalesOrder = new DataTable("dtSO");
DataColumn colItemCode = new DataColumn("ItemCode",typeof(string));
......
tableSalesOrder.Columns.Add(colItemCode);
......
}
else
{
tableSalesOrder.Clear();
}
Thanks guys for the help. Finally I found where the bugs were.

LinqToExcel Is it possible to read the formula as a string

I've encountered a problem. I'm trying to read data from xlsx document using LinqToExcel. The data is retrieved but the document has cells which are interpreted as formulas with text like "=- Something". Is it possible to get those cells text and not the counted value of the formula?
var book = new LinqToExcel.ExcelQueryFactory(path);
var worksheetNames = book.GetWorksheetNames();
var query =
from row in book.WorksheetNoHeader()
where row[0] != ""
select row;
With LinqToExcel you can't receive formula, because linqtoExcel uses OleDB to access sheet. OleDB doesn't allow to access formulas. Instead linqtoExcel you can use Worksheet.Range Property
Example:
Range range = (Range)sheet.Cells[i, j];
return range.Formula;

How to import data from one column of Excel to listbox using C#

I have an openFileDialog tool. I will choose a excel file from my computer and my programme read a column (for example A column) and write my listbox on GUI. How can i do it via OleDB? I am new at C#. If you explain detailed, I will be happy for that.
Thank you for your help.
In order to use the OLEDB provider successfully we have to consider a few points.
The OLEDB provider for Excel 2003 files is different from the one used for
Excel 2007/2010 files. So, the first thing we have to do
is determining the Excel file format in order to select the correct provider.
In the code example below I simply check the extension of the file to determine
the Excel file format. Please note, that there are more elaborated methods to
determine the Excel file format (e.g. via the magic bytes).
To select all rows of a Excel sheet we need to know the name of
the Excel sheet. The standard sheet names are language dependent and
could be renamed by the user.
So, we need a way to determine the name of the sheets included
in a Excel file to be language independent (and of course independent of renamed sheets).
Fortunately, the OleDbConnection class provides
a method called GetOleDbSchemaTable which allows us to get all
the sheet names in an Excel file.
The OLEDB provider for Excel
supports an extended property called HDR. Setting HDR to Yes
means that the first row of a sheet contains the column titles.
So, if you use column titles you should set
HDR=Yes.
So, to summarize the code sample below does the following (on button click):
Determines the Excel file type based on the file extension.
Selects the correct OLEDB provider based on the excel file type to build the connection string.
Determines the sheet names included in the Excel file.
Selects the first sheet, selects all rows and stores the rows in a DataTable called mytable.
Displays all values of the first column in a listbox called listbox1.
Code sample:
private static bool IsExcelXmlFileFormat(string fileName)
{
return fileName.EndsWith("xlsx", StringComparison.OrdinalIgnoreCase);
}
private void button1_Click(object sender, EventArgs e)
{
// Open your FileOpenDialog and let the user select a file...
string fileName = "c:\\temp\\myexcelfile.xlsx";
OleDbConnectionStringBuilder connStringBuilder =
new OleDbConnectionStringBuilder();
connStringBuilder.DataSource = fileName;
if (IsExcelXmlFileFormat(fileName))
{
// Set HDR=Yes if first row contains column titles.
connStringBuilder.Provider = "Microsoft.ACE.OLEDB.12.0";
connStringBuilder.Add("Extended Properties", "Excel 8.0;HDR=NO;");
}
else
{
connStringBuilder.Provider = "Microsoft.Jet.OLEDB.4.0";
connStringBuilder.Add("Extended Properties", "Excel 8.0;");
}
DataSet data = new DataSet();
using (OleDbConnection dbConn = new OleDbConnection(connStringBuilder.ConnectionString))
{
dbConn.Open();
DataTable sheets = dbConn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
using (OleDbCommand selectCmd = new OleDbCommand(
String.Format("SELECT * FROM [{0}]", sheets.Rows[0]["TABLE_NAME"]), dbConn))
{
using (OleDbDataAdapter dbAdapter = new OleDbDataAdapter())
{
dbAdapter.SelectCommand = selectCmd;
dbAdapter.Fill(data, "mytable");
}
}
}
// To enumerate all rows use the following code.
// foreach (DataRow row in data.Tables["mytable"].Rows)
// {
// Console.Out.WriteLine(row[0]);
// }
// Display the values of column 0 in a listbox called listBox1.
listBox1.ValueMember = data.Tables["mytable"].Columns[0].ColumnName;
listBox1.DisplayMember = data.Tables["mytable"].Columns[0].ColumnName;
listBox1.DataSource = data.Tables["mytable"];
}

Why is first empty line ignored in Excel import with OleDb

With .Net's OleDb I try to import an Excel table in which the first row(s) can be empty. I want to keep the empty row in the DataTable to be able to map the cells to Excel-style cell names "A1, A2, ..." later. But the first line is removed, no matter what I do.
Excel file looks like:
- - -
ABC XY ZZ
1 2 3
4 4 5
Where "-" is an empty cell. (I have no influence to the import format.)
Simplified code:
string cnnStr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=\"file.xls\";Extended Properties=\"Excel 8.0;HDR=No;IMEX=1\"";
string mySheet = "Sheet1$";
OleDbConnection connection = new OleDbConnection(cnnStr);
DataSet Contents = new DataSet();
using (OleDbDataAdapter adapter = new OleDbDataAdapter("select * from [" + mySheet + "]", connection))
{
adapter.Fill(Contents);
}
Console.WriteLine(Contents.Tables[0].Rows.Count); // prints: 3
Console.WriteLine(Contents.Tables[0].Rows[0].ItemArray[0]); // prints: ABC
Any idea how to preserve that empty row?
ps: I found How to count empty rows when reading from Excel but couldn't reproduce it.
The issue seems to be related to the TypeGuessRows feature of the OLEDB provider. In a nutshell, data in an Excel column can be of any type. The OLEDB provider guesses the data type by scanning the first 8 rows of the sheet to determine the Majority Type - the data type with the most number of values in the sample. Anything that is not of the Majority Type are discarded.
See this blog post for a more detailed explanation.
As well as this MS KB Article that discusses the behavior.
(Skip down to the Workaround section for the TypeGuessRows behavior)
As a test, I created a file similar to the sample you posted but formatted all of the columns as text and saved the file. Running the code you posted I was able to see 4 Rows returned, with the first Row an empty string.
You may also want to try modifying the registry to see if changing the TypeGuessRows setting to 0 (scan all data in the file to determine data type of each column) helps return the first blank row. My hunch is that this won't help though.
OleDbDataAdapter considers the first row as header.
In order to get the first row, create a datarow from the header of the datatable.
And insert at the first location.
DataTable dt = Contents.Tables[0];
DataRow dr = new DataRow();
int i = 0;
foreach (DataColumn column in dt.Columns)
{
dr[i] = column.ColumnName.ToString();
i++;
}
dt.Rows.InsertAt(dr, 0);

When reading a CSV file using a DataReader and the OLEDB Jet data provider, how can I control column data types?

In my C# application I am using the Microsoft Jet OLEDB data provider to read a CSV file. The connection string looks like this:
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=c:\Data;Extended Properties="text;HDR=Yes;FMT=Delimited
I open an ADO.NET OleDbConnection using that connection string and select all the rows from the CSV file with the command:
select * from Data.csv
When I open an OleDbDataReader and examine the data types of the columns it returns, I find that something in the stack has tried to guess at the data types based on the first row of data in the file. For example, suppose the CSV file contains:
House,Street,Town
123,Fake Street,Springfield
12a,Evergreen Terrace,Springfield
Calling the OleDbDataReader.GetDataTypeName method for the House column will reveal that the column has been given the data type "DBTYPE_I4", so all values read from it are interpreted as integers. My problem is that House should be a string - when I try to read the House value from the second row, the OleDbDataReader returns null.
How can I tell either the Jet database provider or the OleDbDataReader to interpret a column as strings instead of numbers?
To expand on Marc's answer, I need to create a text file called Schema.ini and put it in the same directory as the CSV file. As well as column types, this file can specify the file format, date time format, regional settings, and the column names if they're not included in the file.
To make the example I gave in the question work, the Schema file should look like this:
[Data.csv]
ColNameHeader=True
Col1=House Text
Col2=Street Text
Col3=Town Text
I could also try this to make the data provider examine all the rows in the file before it tries to guess the data types:
[Data.csv]
ColNameHeader=true
MaxScanRows=0
In real life, my application imports data from files with dynamic names, so I have to create a Schema.ini file on the fly and write it to the same directory as the CSV file before I open my connection.
Further details can be found here - http://msdn.microsoft.com/en-us/library/ms709353(VS.85).aspx - or by searching the MSDN Library for "Schema.ini file".
There's a schema file you can create that would tell ADO.NET how to interpret the CSV - in effect giving it a structure.
Try this: http://www.aspdotnetcodes.com/Importing_CSV_Database_Schema.ini.aspx
Or the most recent MS Documentation
Please check
http://kbcsv.codeplex.com/
using (var reader = new CsvReader("data.csv"))
{
reader.ReadHeaderRecord();
foreach (var record in reader.DataRecords)
{
var name = record["Name"];
var age = record["Age"];
}
}
You need to tell the driver to scan all rows to determine the schema. Otherwise if the first few rows are numeric and the rest are alphanumeric, the alphanumeric cells will be blank.
Like Rory, I found that I needed to create a schema.ini file dynamically because there is no way to programatically tell the driver to scan all rows. (this is not the case for excel files)
You must have MaxScanRows=0 in your schema.ini
Here's a code example:
public static DataTable GetDataFromCsvFile(string filePath, bool isFirstRowHeader = true)
{
if (!File.Exists(filePath))
{
throw new FileNotFoundException("The path: " + filePath + " doesn't exist!");
}
if (!(Path.GetExtension(filePath) ?? string.Empty).ToUpper().Equals(".CSV"))
{
throw new ArgumentException("Only CSV files are supported");
}
var pathOnly = Path.GetDirectoryName(filePath);
var filename = Path.GetFileName(filePath);
var schemaIni =
$"[{filename}]{Environment.NewLine}" +
$"Format=CSVDelimited{Environment.NewLine}" +
$"ColNameHeader={(isFirstRowHeader ? "True" : "False")}{Environment.NewLine}" +
$"MaxScanRows=0{Environment.NewLine}" +
$" ; scan all rows for data type{Environment.NewLine}" +
$" ; This file was automatically generated";
var schemaFile = pathOnly != null ? Path.Combine(pathOnly, "schema.ini") : "schema.ini";
File.WriteAllText(schemaFile, schemaIni);
try
{
var sqlCommand = $#"SELECT * FROM [{filename}]";
var oleDbConnString =
$"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={pathOnly};Extended Properties=\"Text;HDR={(isFirstRowHeader ? "Yes" : "No")}\"";
using (var oleDbConnection = new OleDbConnection(oleDbConnString))
using (var adapter = new OleDbDataAdapter(sqlCommand, oleDbConnection))
using (var dataTable = new DataTable())
{
adapter.FillSchema(dataTable, SchemaType.Source);
adapter.Fill(dataTable);
return dataTable;
}
}
finally
{
if (File.Exists(schemaFile))
{
File.Delete(schemaFile);
}
}
}
You'll need to do some modification if you are running this on the same directory in multiple threads at the same time.

Categories

Resources