I have a spreadsheet and I want to upload it in a ASP.NET-MVC tool using C# to extract the data then put them on an SQL server database.
I created a function who put the data into a DataSet so I can use it after to put the data into the database.
Here is the function :
public DataSet getData(HttpPostedFileBase file, string path)
{
var fileName = Path.GetFileName(file.FileName);
oledbConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + path + ";Extended Properties=\"Excel 8.0;HDR=No;IMEX=1\"");
DataSet ds = new DataSet();
OleDbCommand cmd = new OleDbCommand();
OleDbDataAdapter oleda = new OleDbDataAdapter();
oledbConn.Open();
cmd.Connection = oledbConn;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "SELECT * FROM [Worksheet0$]";
oleda = new OleDbDataAdapter(cmd);
oleda.Fill(ds);
oledbConn.Close();
return ds;
}
Everything is working but when I do a foreach on the dataset and retrieve the data there is a formatting problem.
The values in my spreadsheet are formatted as Numbers, so for example 1.25 turns into 1.3. The cell is showing 1.3 but when I click on it the value is 1.25.
When I check on my dataset the values in it are the one formatted (not the real values), I have for example the 1.3 instead the 1.25.
When I change the columns format before uploading, everything works all right ! But I am looking for an automatic process to do that.
I think you should try to change this library. I recommend you to use ExcelDataReader 2.1.2.3 and here is the NuGet for it: https://www.nuget.org/packages/ExcelDataReader/
I used this library, it is very fast, and lightweight library. and here is my code:
public List<Checklist> Provide()
{
List<Checklist> checklists = new List<Checklist>();
using (var reader = ExcelReaderFactory.CreateOpenXmlReader(m_Stream))
{
while (reader.Read())
{
Checklist checklist = new Checklist();
checklist.Description = reader.GetString(1);
checklist.View = reader.GetString(2);
checklist.Organ = reader.GetString(3);
checklists.Add(checklist);
}
return checklists;
}
}
Thanks to #Nadeem Khouri
I used the ExcelDataReaderLibrary
Here is the working code :
public DataSet getData(string path)
{
FileStream stream = File.Open(path, FileMode.Open, FileAccess.Read);
IExcelDataReader excelReader = ExcelReaderFactory.CreateBinaryReader(stream);
DataSet result = excelReader.AsDataSet();
return result;
}
Related
i am trying to read excel data to C# using ODBC here is my code
string lstrFileName = "Sheet1";
//string strConnString = "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq="+path+ ";Extensions=asc,csv,tab,txt;Persist Security Info=False";
string strConnString = "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};Dbq=E:\\T1.xlsx;Extensions=xls/xlsx;Persist Security Info=False";
DataTable ds;
using (OdbcConnection oConn = new OdbcConnection(strConnString))
{
using (OdbcCommand oCmd = new OdbcCommand())
{
oCmd.Connection = oConn;
oCmd.CommandType = System.Data.CommandType.Text;
oCmd.CommandText = "select A from [" + lstrFileName + "$]";
OdbcDataAdapter oAdap = new OdbcDataAdapter();
oAdap.SelectCommand = oCmd;
ds = new DataTable();
oAdap.Fill(ds);
oAdap.Dispose();
// ds.Dispose();
}
}
my sample data
A
1
2
3
AA
BB
its data table its read 1,2,3 and two blank row
i can understand because of first row its deciding data type , but how can i convert as String and read all row .
Any suggestion .
i Already tried CStr but no help .
For a previous discussion of similar problem here, please check following:
DBNull in non-empty cell when reading Excel file through OleDB
As a workaround, you may also format the column as "text"(i.e. in Excel, select column, right click "Format Cells..."), though this might be impractical if you will process large number of files or if you must not touch the file..
This is partially speculation, but when reading an Excel document as a database, the adapter has to make a judgement on datatypes and usually does a pretty good job. However, because Excel allows mixed datatypes (and databases do not), it occasionally gets it wrong.
My recommendation would to be to not use a data adapter, and just read in every field as an object type. From there, you can easily cast them to strings (StringBuilder, ToString(), etc) or even TryParse into fields you suspect they should be, ignoring the ODBC datatype.
Something like this would be a boilerplate for that:
using (OdbcCommand oCmd = new OdbcCommand())
{
oCmd.Connection = oConn;
oCmd.CommandType = System.Data.CommandType.Text;
oCmd.CommandText = "select A from [" + lstrFileName + "$]";
using (OdbcDataReader reader = oCmd.ExecuteReader())
{
object[] fields = new object[reader.FieldCount];
while (reader.Read())
{
reader.GetValues(fields);
// do something with fields
}
}
}
Additional information: The Microsoft Office Access database engine could not find the object 'C:\Users\username\Documents\sampleData.xls'. Make sure the object exists and that you spell its name and the path name correctly.
The Error is highlighted at
theDataAdapter.Fill(spreadSheetData);
Here's the sample data I used (tried in .csv , .xls , .xlsx )
Name Age Status Children
Johnny 34 Married 3
Joey 21 Single 1
Michael 16 Dating 0
Smith 42 Divorced 4
Here's the code associated:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO;
using System.Data.OleDb;
namespace uploadExcelFile
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnImport_Click(object sender, EventArgs e)
{
var frmDialog = new System.Windows.Forms.OpenFileDialog();
if (frmDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string strFileName = frmDialog.FileName;
System.IO.FileInfo spreadSheetFile = new System.IO.FileInfo(strFileName);
scheduleGridView.DataSource = spreadSheetFile.ToString();
System.Diagnostics.Debug.WriteLine(frmDialog.FileName);
System.Diagnostics.Debug.WriteLine(frmDialog.SafeFileName);
String name = frmDialog.SafeFileName;
String constr = String.Format(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=""Excel 12.0 Xml;HDR=YES""", frmDialog.FileName);
OleDbConnection myConnection = new OleDbConnection(constr);
OleDbCommand onlineConnection = new OleDbCommand("SELECT * FROM [" + frmDialog.FileName + "]", myConnection);
myConnection.Open();
OleDbDataAdapter theDataAdapter = new OleDbDataAdapter(onlineConnection);
DataTable spreadSheetData = myConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
theDataAdapter.Fill(spreadSheetData);
scheduleGridView.DataSource = spreadSheetData;
}
}
}
}
scheduleGridView is the DataGridViews name, & btnImport is the name for the import Button.
I've installed 2007 Office System Driver: Data Connectivity Components; which gave me the AccessDatabaseEngine.exe, but from there I've been stuck here without understanding how to get around this. It should go without saying that the filepath is correct in its entirety. There is no odd characters in the path name either (spaces, underlines, etc)
Mini Update :: (another dead end it seems like)
Although the initial error says, "could not find the object 'C:\Users\username\Documents\sampleData.xls'"
In the Debugger the exception is read as
When I look at details the exception as "C:\Users\username\Documents\sampleData.xls"
So I thought the error was that it wasn't taking the path as a literal, but this article C# verbatim string literal not working. Very Strange backslash always double
Shows very clearly that that is not my issue.
I am guessing you may be mistaken by what is returned from the following line of code…
DataTable spreadSheetData = myConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
The DataTable returned from this line will have nine (9) columns (TABLE_CATALOG, TABLE_SCHEMA, TABLE_NAME, TABLE_TYPE, TABLE_GUID, DESCRIPTION, TABLE_PROPID, DATE_CREATED and DATE_MODIFIED). This ONE (1) DataTable returned simply “Describes” the worksheet(s) and named range(s) in the entire selected Excel workbook. Each row in this DataTable represent either a worksheet OR a named range. To distinguish worksheets from named ranges, the “TABLE_NAME” column in this DataTable has the name of the worksheet or range AND ends each “Worksheet” Name with a dollar sign ($). If the “TABLE_NAME” value in a row does NOT end in dollar sign, then it is a range and not a worksheet.
Therefore, when the line
OleDbDataAdapter theDataAdapter = new OleDbDataAdapter(onlineConnection);
Blows up and says it cannot file the “filename” error… is somewhat expected because this line is looking for a “worksheet” name, not a filename. On the line creating the select command…
OleDbCommand onlineConnection = new OleDbCommand("SELECT * FROM [" + frmDialog.FileName + "]", myConnection);
This is incorrect; you have already selected the filename and open the file with
String constr = String.Format(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=""Excel 12.0 Xml;HDR=YES""", frmDialog.FileName);
OleDbConnection myConnection = new OleDbConnection(constr);
myConnection.Open();
The correct OleDbCommand line should be…
OleDbCommand onlineConnection = new OleDbCommand("SELECT * FROM [" + sheetName + "]", myConnection);
The problem here is that the current code is not getting the worksheet names. Therefore, we cannot “select” the worksheet from the workbook then fill the adapter with the worksheet.
The other issue is setting the DataGridView’s DataSource to spreadSheetData… when you get the worksheet(s) from an Excel “Workbook”, you must assume there will be more than one sheet. Therefore a DataSet will work as a container to hold all the worksheets in the workbook. Each DataTable in the DataSet would be a single worksheet and it can be surmised that the DataGridView can only display ONE (1) of these tables at a time. Given this, below are the changes described along with an added button to display the “Next” worksheet in the DataGridView since there may be more than one worksheet in the workbook. Hope this makes sense.
int sheetIndex = 0;
DataSet ds = new DataSet();
public Form1() {
InitializeComponent();
}
private void btnImport_Click(object sender, EventArgs e) {
var frmDialog = new System.Windows.Forms.OpenFileDialog();
if (frmDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK) {
String constr = String.Format(#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=""Excel 12.0 Xml;HDR=YES""", frmDialog.FileName);
OleDbConnection myConnection = new OleDbConnection(constr);
myConnection.Open();
DataTable spreadSheetData = myConnection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
string sheetName = "";
DataTable dt;
OleDbCommand onlineConnection;
OleDbDataAdapter theDataAdapter;
// fill the "DataSet" each table in the set is a worksheet in the Excel file
foreach (DataRow dr in spreadSheetData.Rows) {
sheetName = dr["TABLE_NAME"].ToString();
sheetName = sheetName.Replace("'", "");
if (sheetName.EndsWith("$")) {
onlineConnection = new OleDbCommand("SELECT * FROM [" + sheetName + "]", myConnection);
theDataAdapter = new OleDbDataAdapter(onlineConnection);
dt = new DataTable();
dt.TableName = sheetName;
theDataAdapter.Fill(dt);
ds.Tables.Add(dt);
}
}
myConnection.Close();
scheduleGridView.DataSource = ds.Tables[0];
setLabel();
}
}
private void setLabel() {
label1.Text = "Showing worksheet " + sheetIndex + " Named: " + ds.Tables[sheetIndex].TableName + " out of a total of " + ds.Tables.Count + " worksheets";
}
private void btnNextSheet_Click(object sender, EventArgs e) {
if (sheetIndex == ds.Tables.Count - 1)
sheetIndex = 0;
else
sheetIndex++;
scheduleGridView.DataSource = ds.Tables[sheetIndex];
setLabel();
}
I solved it. Well there was a workaround. I used the Excel Data Reader found in this thread: How to Convert DataSet to DataTable
Which led me to https://github.com/ExcelDataReader/ExcelDataReader
^ The readme was fantastic, just went to solution explorer, right click on references, manage NuGet Packages, select browse in the new box, enter ExcelDataReader, then in the .cs file be sure to include, "using Excel;" at the top, the code mentioned in the first link was essentially enough, but here's my exact code for those wondering.
var frmDialog = new System.Windows.Forms.OpenFileDialog();
if (frmDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
/*string strFileName = frmDialog.FileName;
//System.IO.FileInfo spreadSheetFile = new System.IO.FileInfo(strFileName);
System.IO.StreamReader reader = new System.IO.StreamReader(strFileName);
*/
string strFileName = frmDialog.FileName;
FileStream stream = File.Open(strFileName, FileMode.Open, FileAccess.Read);
//1. Reading from a binary Excel file ('97-2003 format; *.xls)
IExcelDataReader excelReader = ExcelReaderFactory.CreateBinaryReader(stream);
//...
//2. Reading from a OpenXml Excel file (2007 format; *.xlsx)
//IExcelDataReader excelReader = ExcelReaderFactory.CreateOpenXmlReader(stream);
//...
//3. DataSet - The result of each spreadsheet will be created in the result.Tables
//DataSet result = excelReader.AsDataSet();
//...
//4. DataSet - Create column names from first row
excelReader.IsFirstRowAsColumnNames = true;
DataSet result = excelReader.AsDataSet();
DataTable data = result.Tables[0];
//5. Data Reader methods
while (excelReader.Read())
{
//excelReader.GetInt32(0);
}
scheduleGridView.DataSource = data;
excelReader.Close();
Sorry for my spelling, I create so C# form application with local database. I created simple code which worked fine:
SqlCeConnection conn = new SqlCeConnection("Data Source = 4.sdf");
Microsoft.Office.Interop.Excel.Application xls = new Microsoft.Office.Interop.Excel.Application();
Workbook vb = xls.Workbooks.Add(XlSheetType.xlWorksheet);
Worksheet vs = (Worksheet)xls.ActiveSheet;
String t = "SELECT d.[regnr], d.[dalisid],d.[kiekis],d.[kaina] FROM [bilietas] AS b, [dalispard] AS d WHERE b.[regnr]=d.[regnr]";
SqlCeCommand cmd = new SqlCeCommand(t, conn);
conn.Open();
SqlCeDataReader rdr = cmd.ExecuteReader();
int i=2;
object aa, bb, cc, dd;
vs.Cells[1, 1] = "Reg. NR.";
vs.Cells[1, 2] = "Dalies ID";
vs.Cells[1, 3] = "Kiekis";
vs.Cells[1, 4] = "Kaina";
try
{
while (rdr.Read())
{
aa = rdr.GetValue(0);
bb = rdr.GetValue(1);
cc = rdr.GetValue(2);
dd = rdr.GetValue(3);
vs.Cells[i, 1] = aa.ToString();
vs.Cells[i, 2] = bb.ToString();
vs.Cells[i, 3] = cc.ToString();
vs.Cells[i, 4] = dd.ToString();
i++;
}
}
finally { conn.Close(); rdr.Close(); }
xls.Visible = true;
it bugged and started write emty cells on excel file when I edited input button for refreshing datagridview when input to table are saved by adding:
string eilute = "SELECT * FROM bilietas";
SqlCeCommand cmdd = new SqlCeCommand(eilute, conn);
conn.Open();
try
{
dt.Load(cmdd.ExecuteReader());
BindingSource bs = new BindingSource();
bs.DataSource = dt;
bilietasDataGridView.DataSource = bs;
MessageBox.Show("Atlikta", "Atlikta", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
finally { conn.Close(); }
So I really have no idea why it not working anymore.
Using Excel interop is not the best idea in your case - it is slow and requires Excel installed. Try other approach - OleDbConnection way. You can write data to Excel worksheet using plain SQL INSERT command. Create OleDbConnection object with the following connection string:
"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=SAMPLE.XLSX;Extended Properties=\"Excel 12.0 XML\";"
After that create new OleDbCommand with text like this:
"insert into [YOR_WORKBOOK_NAME$] ([COLUMN_NAME]) values ('Sample text')"
Execute command with ExecuteNonQuery method. Repeat this command with different values to insert many rows to your workbook. This method is much faster than interop approach.
I am reading .xlsx file using c# like this
string strConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + fileName +
";Extended Properties=\"Excel 12.0;HDR=No;IMEX=1\";";
var output = new DataSet();
using (var conn = new OleDbConnection(strConn))
{
conn.Open();
var dt = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });
foreach (DataRow row in dt.Rows)
{
string sheet = row["TABLE_NAME"].ToString();
var cmd = new OleDbCommand("SELECT * FROM [+"+sheet+"+]", conn);
cmd.CommandType = CommandType.Text;
OleDbDataAdapter xlAdapter = new OleDbDataAdapter(cmd);
xlAdapter.Fill(output,"School");
}
}
But I am getting error at xlAdapter.Fill(output,"School");
Error is
The Microsoft Office Access database engine could not find the object '+_xlnm.Print_Area+'. Make sure the object exists and that you spell its name and the path name correctly.
I am not able to figure out that what is wrong happening in code.
I believe your sheet is named _xlnm.Print_Area.
Try changing this line
var cmd = new OleDbCommand("SELECT * FROM [+"+sheet+"+]", conn);
to
var cmd = new OleDbCommand("SELECT * FROM ["+sheet+"]", conn);
When you define print area in your sheet "_xlnm.Print_Area" automatic added with your new sheet. Please remove print area form excel sheet or use following code
if (!dr["TABLE_NAME"].ToString().Contains("_xlnm#Print_Area"))
{
obj.SheetName = dr["TABLE_NAME"].ToString();
lst.Add(obj);
}
variable sheet contains value: +_xlnm.Print_Area+
This +_xlnm.Print_Area+ does not actually exists.
Thats why error is comming.
Check with that object.
I would check what you are getting in row["TABLE_NAME"].ToString(); values. Alternatively, you can try OpenXML SDK: How to: Parse and read a large spreadsheet document (Open XML SDK)
I want to read a simple foxpro dbf file and convert it into xml file and save it into my pc.
Is it possible to read and convert simple file.DBF with out using any db connection?
Yes, It is possible. Create connection on DBF table as appropriate based on this link http://www.connectionstrings.com/dbf-foxpro. Later you get the entire data onto a Dataset. You can save data set wherever you want to in XML format.
Here is the code...
private void btnBrowse_Click(object sender, EventArgs e)
{
try
{
var path = "F:\\Projects\\dbf"; // Path of the folder containing dbf file.
var fileName = "Invoices1.dbf";
var constr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + path + ";Extended Properties=DBASE III";
var sql = "select * from " + fileName;
var ds = new DataSet();
using (var con = new OleDbConnection(constr))
{
con.Open();
using (var cmd = new OleDbCommand(sql, con))
{
using (var da = new OleDbDataAdapter(cmd))
{
da.Fill(ds);
dataGridView1.DataSource = ds.Tables.Count > 0
? ds.Tables[0].Copy() : new DataTable();
}
}
}
}
catch
{
throw;
}
}