Change cell value of excel file in c#? - c#

I have excel file and loaded in c# windows applciaction.
I want to change the value in excel cell e.g change value in cell a10 and save the file.
The excel file contains multiple sheets.
Any help in this regard?
var ds = new DataSet();
ds = Parse(fileName);
static DataSet Parse(string fileName)
{
string connectionString = string.Format("provider=Microsoft.Jet.OLEDB.4.0; data source={0};Extended Properties=Excel 8.0;", fileName);
DataSet data = new DataSet();
foreach (var sheetName in GetExcelSheetNames(connectionString))
{
using (OleDbConnection con = new OleDbConnection(connectionString))
{
var dataTable = new DataTable();
string query = string.Format("SELECT * FROM [{0}]", sheetName);
con.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter(query, con);
adapter.Fill(dataTable);
data.Tables.Add(dataTable);
}
}
return data;
}
static string[] GetExcelSheetNames(string connectionString)
{
OleDbConnection con = null;
DataTable dt = null;
con = new OleDbConnection(connectionString);
con.Open();
dt = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
if (dt == null)
{
return null;
}
String[] excelSheetNames = new String[dt.Rows.Count];
int i = 0;
foreach (DataRow row in dt.Rows)
{
excelSheetNames[i] = row["TABLE_NAME"].ToString();
i++;
}
return excelSheetNames;
}
}

To specify that your sheet has a header row or not, modify the connection string to specify the HDR value. Refer to http://www.connectionstrings.com/excel/ for more information.
If your sheet has a header row, you can refer the columns by the header.
If your sheet does not have a header row use F1, F2, F3.... Fn where F1 is the first selected column. If you don't specify where to start, then column A, B, C correspond to F1, F2, F3 etc.
e.g.
SELECT * FROM [Sheet1$] <-- Column A=F1, B=F2 etc.
SELECT * FROM [Sheet1$B1:Z100] <-- Column B=F1, C=F2 etc.
Now once you know how to refer to the columns, rest should be easy. Create an OledbCommand object and execute your command.
UPDATE [Sheet1$A1:A1] SET F1='TestValue1' <-- trick to update only one cell
UPDATE [Sheet1$] SET F1='TestValue1', F2 = 'some value 2' WHERE WhateverCondition
I haven't ever tried with Datasets and DataAdapters with excel oledb, but logically that should work too because in the end they all drill down to Command object.

Related

How to Read an Excel Table placed not in First Cell

I have an Excel workbook where the Table is placed after the 9th row in the worksheet. How am I supposed to read the Table at that point?
Currently, I am able to read the Excel worksheet using Microsoft.ACE.OLEDB.12.0 provider like this:
OleDbConnection connection = new OleDbConnection();
var connectionString = $"Provider=Microsoft.ACE.OLEDB.12.0; data source={fileName}; Extended Properties=Excel 8.0;";
connection.ConnectionString = connectionString;
connection.Open();
DataTable dbSchema = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
if (dbSchema == null || dbSchema.Rows.Count < 1)
{
throw new Exception("Error: Could not determine the name of the first worksheet.");
}
string firstSheetName = dbSchema.Rows[0]["TABLE_NAME"].ToString();
var adapter = new OleDbDataAdapter($"SELECT * FROM [{firstSheetName}]", connectionString);
var ds = new DataSet();
adapter.Fill(ds, "anyNameHere");
DataTable table = ds.Tables[0];
MessageBox.Show($"No of Records found: {table.Rows.Count}");
What I observe in the above code is that the Table is read but null values are yielded for non-table fields. However, I will need to do a filter for Row Number after n ( if n is the place where the table is placed) if I am supposed to get the intended.
I would welcome if this can be achieved by other means instead of OleDbConnection
the Table is placed after the 9th row in the worksheet
So you know the index of the heading
I would welcome if this can be achieved by other means instead of OleDbConnection
Actually, I use ExcelDataReader.Mapping, you can specify the row of the heading, here is how it works:
I have this data in an excel file
Model
public class SheetData
{
public string Name { set; get; }
public int Value { set; get; }
}
Usage (note that HeadingIndex takes value of 8 = 9 - 1)
using var stream = File.OpenRead(#"C:\Users\mosul\Desktop\Sample.xlsx");
using var importer = new ExcelImporter(stream);
var sheet = importer.ReadSheet();
sheet.HeadingIndex = 8;
var data = sheet.ReadRows<SheetData>().ToList();
Console.WriteLine(data.Count); // 3
That's it.

how to fix duplicate insert of excel records to database

I am inserting into database excel sheet, I have been able to upload with and without sheet names, I just want to know how can I prevent the data from being inserting multiple times e.g. if my sheet has 2 records the loop inserts it twice and the table ends up looking like this:
ID DOB NAME SURNAME
1 1/02/1998 jack turner
2 2/02/1989 jill blue
1 1/02/1998 jack turner
2 2/02/1989 jill blue
Code:
public void up(string sFileName = #"filename") {
string ssqltable = "[dbo].[My_Table]";
//string sFileName = #"filename";
try{
string sConStr = string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties='Excel 8.0;HDR=YES';", sFileName);
DataTable dt = new DataTable();
SqlConnection sqlconn = new SqlConnection(strConnString);
sqlconn.Open();
using (OleDbConnection connection = new OleDbConnection(sConStr))
{
connection.Open();
dt = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });
var sheets = dt.Rows[0].Field<string>("TABLE_NAME");
foreach(var sheet in sheets) //loop through the collection of sheets ;)
{
//your logic here...
string myexceldataquery = string.Format("Select * FROM [{0}]; ", sheets);
//get data
OleDbConnection oledbconn = new OleDbConnection(sConStr);
OleDbCommand oledbcmd = new OleDbCommand(myexceldataquery, oledbconn);
oledbconn.Open();
OleDbDataReader dr = oledbcmd.ExecuteReader();
{
DataTable table = new DataTable("benlist");
table.Load(dr);
// add two extra columns to data table to be added to database table
table.Columns.Add("name",typeof(string));
table.Columns.Add("surname",typeof(string));
// add data to additional columns
foreach (DataRow row in table.Rows){
row["name"] =Session["Username"].ToString();
row["surname"] = Session["Username"].ToString();
}
SqlBulkCopy bulkcopy = new SqlBulkCopy(strConnString);
bulkcopy.DestinationTableName = ssqltable;
////Mapping Table column
bulkcopy.ColumnMappings.Add("IDNumber", "[IDNumber]");
bulkcopy.ColumnMappings.Add("DOB", "[DOB]");
bulkcopy.ColumnMappings.Add("name", "[name]");
bulkcopy.ColumnMappings.Add("surname", "[surname]");
//sqlcmd.ExecuteNonQuery();
//while (dr.Read())
//{
bulkcopy.WriteToServer(table);
//}
connection.Close();
sqlconn.Close();
}
}
}
}
catch (Exception){}
ClientScript.RegisterStartupScript(GetType(), "alert", "alert('File Uploaded');", true);
}
I expect the data to be inserted once no duplicates e.g
ID DOB NAME SURNAME
1 1/02/1998 jack turner
2 2/02/1989 jill blue
so i removed the loop and the data no longer gets duplicated when i insert it into the database table, thanks
reference: Getting the first sheet from an Excel document regardless of sheet name with OleDb
using (OleDbConnection connection = new OleDbConnection(sConStr))
{
connection.Open();
/// get sheet name
dt = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });
//var sheets = dt.Rows[0].Field<string>("TABLE_NAME");
// foreach(var sheet in sheets) //loop through the collection of sheets ;)
// {
var sheets = dt.Rows[0].Field<string>("TABLE_NAME");
//your logic here...
string myexceldataquery = string.Format("Select * FROM [{0}]; ", sheets);
//get data

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

how can i loop in row header for excel files using c# using oledb

Please assist.I am reading excel files.I need to check column headers(customerReferenceNumber) that all cells contains are not empty and no duplicates.
string columHeader ="customerReferenceNumber":
public bool check(string customerReferenceNumber)
{
string cnStr = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\nyathis\Desktop\SBSA_TestData.xlsx;Extended Properties='Excel 12.0 xml;HDR=YES;'";
DataTable dt = new DataTable();
string sql = "Select distinct CustomerReferenceNumber From [Sheet1$] " ;
using (OleDbConnection conn = new OleDbConnection(cnStr))
{
using (OleDbDataAdapter da = new OleDbDataAdapter(sql, conn))
{
da.Fill(dt);
//Columnheader ==CustomerReferenceNumber
if (dt.Columns[0].ColumnName.Equals(stdColumns[0]))
{
//How can I do Logic I need to check if rows in these columnHeader had duplicate and row is not blank!!!!!!!!!
if(dt.Rows is not emptyornull
}
}
}
return false;
}

Dataset to xml Null Values

I have the code below, where from 3 tables I take the data and write an xml.
I want write (when a record column has null value) the column on the xml with null value. For example if (Category_name == Null ) to write on the xml (Null) Right now the code skip the column and don’t even have this column on the xml.
string xmlFileData = "";
string[] tables = new string[] { "category", "company", "config" };
string query;
xmlFileData += "<MyXml>";
SqlConnection conn;
dbconnect obj;
obj = new dbconnect();//initailizing class object
for (int i = 0; i < tables.Length; i++)
{
string ifemptquery;
DataSet ds = new DataSet();
DataSet ds1 = new DataSet();
conn = obj.getConnection(); //calling connection function
ifemptquery = "SELECT * FROM " + tables[i] ";
SqlCommand cmd1 = new SqlCommand(ifemptquery, conn);
conn.Open();
SqlDataAdapter da1 = new SqlDataAdapter(cmd1);
DataTable dt1 = new DataTable();
da1.Fill(dt1);
conn.Close();
if (dt1.Rows.Count > 0)
{
query = "SELECT * FROM " + tables[i] ";
SqlCommand cmd = new SqlCommand(query, conn);
conn.Open();
SqlDataAdapter da = new SqlDataAdapter(cmd);
da.Fill(ds);
conn.Close();
conn.Dispose();
ds.DataSetName = tables[i];
string vartbname = tables[i];
string trimed_tbname = vartbname.Replace("_", "");
ds.Tables[0].TableName = trimed_tbname;
xmlFileData += ds.GetXml();
}
else
{
}
}
xmlFileData += "</MyXml>";
File.WriteAllText(Server.MapPath("~/xmlbackup/") + "Backup.xml", xmlFileData);
I have been searching the whole world for a solution of writing null fields to XML using DataSet.WriteXML(). The answer posted by Vlad is the one I also used in my project but I found that following works in a much more performance optimized way. I have created a function for your convenience. Change your dataset tables one after the other by calling the following function and replacing the tables.
private DataTable GetNullFilledDataTableForXML(DataTable dtSource)
{
// Create a target table with same structure as source and fields as strings
// We can change the column datatype as long as there is no data loaded
DataTable dtTarget = dtSource.Clone();
foreach (DataColumn col in dtTarget.Columns)
col.DataType = typeof(string);
// Start importing the source into target by ItemArray copying which
// is found to be reasonably fast for nulk operations. VS 2015 is reporting
// 500-525 milliseconds for loading 100,000 records x 10 columns
// after null conversion in every cell which may be usable in many
// circumstances.
// Machine config: i5 2nd Gen, 8 GB RAM, Windows 7 64bit, VS 2015 Update 1
int colCountInTarget = dtTarget.Columns.Count;
foreach (DataRow sourceRow in dtSource.Rows)
{
// Get a new row loaded with data from source row
DataRow targetRow = dtTarget.NewRow();
targetRow.ItemArray = sourceRow.ItemArray;
// Update DBNull.Values to empty string in the new (target) row
// We can safely assign empty string since the target table columns
// are all of string type
for (int ctr = 0; ctr < colCountInTarget; ctr++)
if (targetRow[ctr] == DBNull.Value)
targetRow[ctr] = String.Empty;
// Now add the null filled row to target datatable
dtTarget.Rows.Add(targetRow);
}
// Return the target datatable
return dtTarget;
}
Refer similar question here - dataSet.GetXml() doesn't return xml for null or blank columns
Apart from solutions mentioned there, you can also traverse through dataset and write XML using XmlTextWriter. This method is not recommended if you are dealing with huge data.

Categories