User select which excel sheet to be loaded in C# - c#

I want to read an excel file. This excel file have multiple sheets. The user have the choice to select which sheet is loaded in c# to work with it. I want to copy data into access database.
I have written the code for loading excel workbook and for copying data into access database table. But in this code only user select the Excel file not the worksheet, i have hard coded the worksheet name Seven in code.
How to give functionality for user to select his own choice sheet in
workbook excel. Also i want to retrieve all worksheets in excel file.
Code is under
private void btnImport_Click(object sender, EventArgs e)
{
var openFileExcel = new OpenFileDialog()
{
Filter = "Excel Files | *.xlsx; *.xls; *.xlsm",
Title = "Select an Excel File",
CheckFileExists = true
};
if (openFileExcel.ShowDialog() == DialogResult.Cancel)
return;
DatabaseObjects.FileName = openFileExcel.FileName;
using(OleDbConnection conn = new OleDbConnection(DatabaseObjects.ConnectionString),
connExcel = new OleDbConnection(DatabaseObjects.ConnectionStringExcel))
{
string query = "INSERT INTO Students (RollNo, SName, FName, ClassID) VALUES(#RollNo, #SName, #FName, #ClassID)";
string queryExcel = "SELECT * FROM [Seven$]";
using (OleDbCommand command = new OleDbCommand(query, conn), commandExcel = new OleDbCommand(queryExcel,connExcel))
{
OleDbParameter param1 = new OleDbParameter("RollNo", OleDbType.Numeric);
command.Parameters.Add(param1);
OleDbParameter param2 = new OleDbParameter("SName", OleDbType.VarChar);
command.Parameters.Add(param2);
OleDbParameter param3 = new OleDbParameter("FName", OleDbType.VarChar);
command.Parameters.Add(param3);
OleDbParameter param4 = new OleDbParameter("ClassID", OleDbType.Numeric);
command.Parameters.Add(param4);
conn.Open();
connExcel.Open();
OleDbDataReader drExcel = commandExcel.ExecuteReader();
while(drExcel.Read())
{
param1.Value = Convert.ToInt32(drExcel[0]);
param2.Value = drExcel[1].ToString();
param3.Value = drExcel[2].ToString();
param4.Value = Convert.ToInt32(drExcel[4]);
command.ExecuteNonQuery();
}
}
}
}

I'm using the office interop classes to retrieve sheets names in excel file
Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook excelBook = xlApp.Workbooks.Open("D:\\Book1.xlsx");
String[] excelSheets = new String[excelBook.Worksheets.Count];
int i = 0;
foreach(Microsoft.Office.Interop.Excel.Worksheet wSheet in excelBook.Worksheets)
{
excelSheets[i] = wSheet.Name;
i++;
}

You can put the list of sheets into a DataTable quite easily.
DataTable dtSheets = conn.GetSchema("Tables")

Pardon me, I went all the way to VBA, since I read the word EXCEL... LOL
Now, to get the list of the existing table in c# you can use
DataTable schemaTable = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
You need conn to be an active connection.
To get the sheets name:
foreach (DataRow dr in schemaTable.Rows)
{
ExcelSheets.Add(dr["TABLE_NAME"].ToString());
}
or, using LINQ
ExcelSheets = schemaTable.AsEnumerable()
.Select(r => r.Field<string>("TABLE_NAME"))
.ToList();

Related

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.

Can only read Excel file when it is actually open in Ms Excel

I am using the following code to open an excel file (XLS) and populate a DataTable with the first worksheet:
var connectionString = string.Format("Provider=Microsoft.Jet.OLEDB.4.0; data source={0}; Extended Properties=Excel 8.0;", filename);
OleDbConnection connExcel = new OleDbConnection(connectionString);
connExcel.Open();
DataTable dtExcelSchema;
dtExcelSchema = connExcel.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
string SheetName = dtExcelSchema.Rows[0]["TABLE_NAME"].ToString();
connExcel.Close();
var adapter = new OleDbDataAdapter("SELECT * FROM [" + SheetName + "]", connectionString);
var ds = new DataSet();
int count = 0;
adapter.Fill(ds, SheetName);
DataTable dt = ds.Tables[0];
It works only when the file is already open in Ms Excel. Why could that be?
If the file is not open, I get an error message (on line connExcel.Open): External table is not in the expected format.
I'm facing the same problem and accordingly to this site, many developers are struggling for the same:
-When I try read Excel with OLE DB all values are empty
-Can't connect to excel file unless file is already open
Actually I'm using the classic connection string (note that I'm trying to read a 97/2003 file):
Provider=Microsoft.Jet.OLEDB.4.0; Data Source = " + GetFilename(filename) + "; Extended Properties ='Excel 8.0;HDR=NO;IMEX=1'
but the file can be read properly only if:
Is open in Excel or even in Word! (the file of course appears corrupted and unreadable, but then the OleDb procedure can read every line of the file), I didn't try with other Office apps
The file is not in read-only mode
I also tried to lock the file manually or to open it with other non-office applications, but the result is not the same. If I follow the two previous rules (file opened in Word or Excel in not read-only mode) I can see all the cells, otherwise it seems the first column is ignored completely (so F2 became F1, F3 became F2,... and F6, the last one, should became F5 otherwise it throws and out-of-index error).
In order to keep compatibility with OleDb without using 3rd parties libraries I found a very stupid workaround using Microsoft.Office.Interop.Excel assembly.
Excel.Application _app = new Excel.Application();
var workbooks = _app.Workbooks;
workbooks.Open(_filename);
// OleDb Connection
using (OleDbConnection conn = new OleDbConnection(connectionOleDb))
{
try
{
conn.Open();
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = conn;
cmd.CommandText = String.Format("SELECT * FROM [{0}$]", tableName);
OleDbDataReader myReader = cmd.ExecuteReader();
int i = 0;
while (myReader.Read())
{
//Here I read through all Excel rows
}
}
catch (Exception E)
{
MessageBox.Show("Error!\n" + E.Message);
}
finally
{
conn.Close();
workbooks.Close();
if (workbooks != null)
System.Runtime.InteropServices.Marshal.ReleaseComObject(workbooks);
_app.Quit();
System.Runtime.InteropServices.Marshal.ReleaseComObject(_app);
}
}
Essentially the first 3 lines run an Excel instance that lasts exactly the time needed to OleDb to perform its tasks.
The last 4 lines, inside the finally block, let the Excel instance to be closed correctly, immediately after the task and avoid ghost Excel processes.
I repeat it's a very stupid workaround that also requires a 1,5 MB dll (Microsoft.Office.Interop.Excel.dll) to be added to the project.
Anyway seems impossible that OleDb cannot manage by itself the missing data...
I had the same problem. If the file was open the read was ok but if the file was closed... some thing was strange... in my case I received strange data from columns and values.. Debugging I found the name of the first sheet and was strange ["xls _xlnm#_FilterDatabase"] looking on the internet I found that's a name of hidden sheet and a trick to avoid read this sheet (HERE) and so I've implemented a method:
private string getFirstVisibileSheet(DataTable dtSheet, int index = 0)
{
string sheetName = String.Empty;
if (dtSheet.Rows.Count >= (index + 1))
{
sheetName = dtSheet.Rows[index]["TABLE_NAME"].ToString();
if (sheetName.Contains("FilterDatabase"))
{
return getFirstVisibileSheet(dtSheet, ++index);
}
}
return sheetName;
}
To me worked very well.
My complete example code is:
string excelFilePath = String.Empty;
string stringConnection = String.Empty;
using (OpenFileDialog openExcelDialog = new OpenFileDialog())
{
openExcelDialog.Filter = "Excel 2007 (*.xlsx)|*.xlsx|Excel 2003 (*.xls)|*.xls";
openExcelDialog.FilterIndex = 1;
openExcelDialog.RestoreDirectory = true;
DialogResult windowsResult = openExcelDialog.ShowDialog();
if (windowsResult != System.Windows.Forms.DialogResult.OK)
{
return;
}
excelFilePath = openExcelDialog.FileName;
using (DataTable dt = new DataTable())
{
try
{
if (!excelFilePath.Equals(String.Empty))
{
stringConnection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + excelFilePath + ";Extended Properties='Excel 8.0; HDR=YES;';";
using (OleDbConnection conn = new OleDbConnection(stringConnection))
{
conn.Open();
OleDbCommand cmd = new OleDbCommand();
cmd.Connection = conn;
DataTable dtSheet = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
string sheetName = getFirstVisibileSheet(dtSheet);
cmd.CommandText = "SELECT * FROM [" + sheetName + "]";
dt.TableName = sheetName;
OleDbDataAdapter da = new OleDbDataAdapter(cmd);
da.Fill(dt);
cmd = null;
conn.Close();
}
}
//Read and Use my DT
foreach (DataRow row in dt.Rows)
{
//On my case I need data on first and second Columns
if ((row.ItemArray.Count() < 2) ||
(row[0] == null || String.IsNullOrWhiteSpace(row[0].ToString()))
||
(row[1] == null ||String.IsNullOrWhiteSpace(row[1].ToString())))
{
continue;
}
//Get the number from the first COL
int colOneNumber = 0;
Int32.TryParse(row[0].ToString(), out colOneNumber);
//Get the string from the second COL
string colTwoString = row[1].ToString();
//Get the string from third COL if is a file path valid
string colThree = (row.ItemArray.Count() >= 3
&& !row.IsNull(2)
&& !String.IsNullOrWhiteSpace(row[2].ToString())
&& File.Exists(row[2].ToString())
) ? row[2].ToString() : String.Empty;
}
}
catch (Exception ex)
{
MessageBox.Show("Import error.\n" + ex.Message, "::ERROR::", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private string getFirstVisibileSheet(DataTable dtSheet, int index = 0)
{
string sheetName = String.Empty;
if (dtSheet.Rows.Count >= (index + 1))
{
sheetName = dtSheet.Rows[index]["TABLE_NAME"].ToString();
if (sheetName.Contains("FilterDatabase"))
{
return getFirstVisibileSheet(dtSheet, ++index);
}
}
return sheetName;
}
Is it failing on ToString(), like here?
Error is "Object reference not set to an instance of an object"
Does Convert.ToString() fix anything?

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;
}

SqlBulkCopy C# Excel all data with sheet names

I want insert Excel sheet data to SQL Server. This Excel data sheet has many work sheet, I want insert all data with excel sheet name. I wrote code it insert two step in my SQL Server table.
Can you please tell me how to insert data with particular sheet name.
E.g.
Name Address WorksheetName
N1 A1 Sheet1
N2 A2 Sheet1
N3 A3 Sheet2
Code:
OleDbConnection oledbconn = new OleDbConnection(excelConnectionString);
oledbconn.Open();
DataTable dt = oledbconn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
if (dt == null)
{
Response.Write("No record Found");
//return;
}
DataRow row = dt.Rows[1];
// Get Excel sheet Name
//String[] SheetName = new String[dt.Rows.Count];
foreach (var sheetName in GetExcelSheetNames(excelConnectionString))
{
for (int c = 0; c < sheetName.Length; c++)
{
string SheetName = row[c].ToString();
string myExcelDataQuery = "SELECT * FROM [" + SheetName + "]";
OleDbCommand oledbcmd = new OleDbCommand(myExcelDataQuery, oledbconn);
OleDbDataReader dr = oledbcmd.ExecuteReader();
string conn = ConfigurationManager.ConnectionStrings["TlcmealsContext"].ConnectionString;
SqlConnection connectionString = new SqlConnection(conn);
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connectionString))
{
bulkCopy.DestinationTableName = "ExcelTemps";
try
{
bulkCopy.WriteToServer(dr);
bulkCopy.WriteToServer(dt.CreateDataReader());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// Close the SqlDataReader. The SqlBulkCopy
// object is automatically closed at the end
// of the using block.
dr.Close();
}
}
}
}
Did you try to use built-in SQL option to import data from EXCEL?
This has worked for me to import data from EXCEL/Access.

Read columns names issue with reader.GetName and OLEDB Excel provider

I have an issue to retrieve the columns names in an Excel sheet.
I have an Excel sheet with only 3 cells in the first row with these 3 values:
in A1: A
in B1: B
in C1: A.B.C
When I try to execute my method the label shows:
A,B,A#B#C
And not:
A,B,A.B.C
My Code:
protected void btnExecute_Click(object sender, EventArgs e)
{
string fullFileName = #"C:\TEST.xls";
List<string> columns = new List<string>();
string connectionString = string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=Excel 8.0;", fullFileName);
using (OleDbConnection conn = new OleDbConnection(connectionString))
{
conn.Open();
// Retrieves the first sheet
DataTable dt = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
string firstSheet = dt.Rows[0]["TABLE_NAME"].ToString();
// Retrieves the list column name
string query = string.Format("SELECT TOP 1 * FROM [{0}]", firstSheet);
OleDbCommand cmd = new OleDbCommand(query, conn);
OleDbDataReader reader = cmd.ExecuteReader();
for (int i = 0; i < reader.FieldCount; i++)
{
columns.Add(reader.GetName(i));
}
}
lblCols.Text = string.Join(",", columns.ToArray());
}
Do you have an idea to fix this issue??
Thanks in advance.
Daniel
Try this OleDBAdapter Excel QA I posted via stack overflow.
I just tested this and it picked up "A.B.C" from a sample .xls you described.
i.e. add the following to the bottom for a quick test:
Object o = ds.Tables["xlsImport"].Rows[0]["LocationID"];
Object oa = ds.Tables["xlsImport"].Rows[0]["PartID"];
Object row0Col3 = ds.Tables["xlsImport"].Rows[0][2];
string valLocationID = o.ToString();
string valPartID = oa.ToString();
string rowZeroColumn3 = row0Col3.ToString();

Categories

Resources