C# memory leak using exel and interop COM - c#

UPDATED 3/12/20. I am struggling with a problem where the Excel application is not releasing following my function call below. I'm not sure what references I'm screwing up but I assume I'm somehow leaving a reference to the application because I see multiple Excel processes in my task manager as the program runs, not to mention large amounts of memory use that don't improve with GC. Thoughts?
static void ParseExcel(string file, SqlConnection conn)
{
Excel.Application xlApp = new Excel.Application();
Excel.Workbook xlWorkbook = xlApp.Workbooks.Open(file);
Excel._Worksheet xlWorksheet = xlWorkbook.Sheets[1];
Excel.Range rng = xlWorksheet.UsedRange;
SqlCommand cmd = conn.CreateCommand();
double colCount = rng.Columns.Count;
double rowCount = rng.Rows.Count;
//iterate over the rows and columns
//start on row 2 since row 1 is column headers
for (int i = 2; i <= rowCount; i++)
{
string companyName = "";
for (int j = 1; j <= colCount; j++)
{
//write the value to the console
if (rng.Cells[i, j] != null && rng.Cells[i, j].Value2 != null)
{
string tst = rng.Cells[i, j].Value2.ToString();
switch (j)
{
case 2:
companyName = tst;
break;
}
} //end of column
}//end of row
//write to database
string sql = "insert into dbo.company( ";
sql += DBI(companyName) + ")";
Console.WriteLine("sql = " + sql);
try
{
cmd.CommandText = sql;
cmd.ExecuteNonQuery();
}catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
xlApp.Quit();
Marshal.ReleaseComObject(rng);
Marshal.ReleaseComObject(xlWorksheet);
Marshal.ReleaseComObject(xlWorkbook);
Marshal.ReleaseComObject(xlApp);
System.Threading.Thread.Sleep(3000);
}
and outside call looks like this:
ParseExcel(#"c:\testfile.xls", conn);
//cleanup
GC.Collect();
GC.WaitForPendingFinalizers();

I should not have made my parse method static. That was the cause of the memory leak.

Related

SQL Server CE not supporting ExecuteSQLCommand with EF?

I've been developing a C# WPF project with VS2015 using SQL Server Express LocalDb with Entity Framework. I have built a custom seeder for the database, that reads test data from an Excel file, that simply combines the Excel data into a command string, and this is inserted using context.Database.ExecuteSQLCommand.
Now, I was thinking of launching the project with SQL Server Compact Edition 4.0, but I find this command is not working anymore. Do I have to write my uploader again using SqlCeConnection and SqlCeCommand or am I missing something?
Also, from somewhere I have understood that with EF you can switch the SQL provider and the code would not need other changes. Am I in for more surprises down the road?
Example of the uploader command:
string cmd = "INSERT INTO Venues(Name, City, Telephone) Values ('X','Y','Z')"
context.Database.ExecuteSqlCommand(cmd);
The error:
There was an error parsing the query. [ Token line number = 2,Token line offset = 1,Token in error = INSERT ]
This is not just a testing issue, as I would want to include this uploader in the production version, too, for quick inserting of master data (e.g. employee list).
EDIT: Uploader code. If this can be done without resorting to raw SQL, that would be a good solution, too.
This loops through Excel sheets (named after entities) and columns (first row has property name) and rows 2->n (data). This handles the upload of basically any amount of data within Excel limitations. The point is that the code has no knowledge of the entities (might have been possible to parameterize DataContext too). Code might not be optimal, as I'm just a beginner, but has worked for me, except not with SQL CE. Editing to suit CE is not a big issue, but I wanted to ask for possibly better ways.
public static class ExcelUploader
{
static ArrayList data;
static List<string> tableNames;
public static string Upload(string filePath)
{
string result = "";
data = new ArrayList();
tableNames = new List<string>();
ArrayList upLoadData = ReadFile(filePath);
List<string> dataList = ArrayListToStringList(upLoadData);
using (var db = new DataContext())
{
using (var trans = db.Database.BeginTransaction())
{
try
{
foreach (var cmd in dataList)
{
Console.WriteLine(cmd);
db.Database.ExecuteSqlCommand(cmd);
}
db.SaveChanges();
trans.Commit();
}
catch (Exception e)
{
trans.Rollback();
result = e.Message;
MessageBox.Show(result);
}
}
}
return result;
}
private static ArrayList ReadFile(string fileName)
{
List<string> commands = new List<string>();
var xlApp = new Microsoft.Office.Interop.Excel.Application();
var wb = xlApp.Workbooks.Open(fileName, ReadOnly: true);
xlApp.Visible = false;
foreach (Worksheet ws in wb.Worksheets)
{
var r = ws.UsedRange;
var array = r.Value;
data.Add(array);
tableNames.Add(ws.Name);
}
wb.Close(SaveChanges: false);
xlApp.Quit();
return data;
}
private static List<string> ArrayListToStringList(ArrayList arrList)
{
List<string> result = new List<string>();
for(int tableAmount = 0;tableAmount<data.Count;tableAmount++)
{
result.Add(ArrayToSqlCommand(arrList[tableAmount] as Array, tableNames[tableAmount]));
}
return result;
}
private static string ArrayToSqlCommand(Array arr, string tableName)
{
int propertyRow = 1;
int firstDataRow = 2;
string command = "";
// loop rows
for (int rowIndex = firstDataRow; rowIndex <= arr.GetUpperBound(0); rowIndex++)
{
command += "INSERT INTO " + tableName + "(";
//add column names
for (int colIndex = 1; colIndex <= arr.GetUpperBound(1); colIndex++)
{
//get property name
command += arr.GetValue(propertyRow, colIndex);
//add comma if not last column, otherwise close bracket
if (colIndex == arr.GetUpperBound(1))
{
command += ") Values (";
}
else
{
command += ", ";
}
}
//add values
for (int colIndex = 1; colIndex <= arr.GetUpperBound(1); colIndex++)
{
//get property value
command += "'" + arr.GetValue(rowIndex, colIndex) + "'";
//add comma if not last column, otherwise close bracket
if (colIndex == arr.GetUpperBound(1))
{
command += ");";
}
else
{
command += ", ";
}
}
command += "\n";
}
return command;
}
}
There are two ways to use raw SQL queries I'd offer.
Initial data
1) Excel table
+=======+=======+===========+
| Name | City | Telephone |
|===========================|
| Adam | Addr1 | 111-11-11 |
|-------|-------|-----------|
| Peter | Addr2 | 222-22-22 |
+-------+-------+-----------+
2) SQL Server CE table
CREATE TABLE Venues
(
Id int identity primary key,
[Name] nvarchar(100) null,
City nvarchar(100) null,
Telephone nvarchar(100) null
);
3) Getting data from Excel
Here we're interested in getting array from Excel sheet. As soon as we get it, we can safely close Excel. The code assumes file "Employees.xlsx" to be next to executable file.
private object[,] GetExcelData()
{
xlApp = new Excel.Application { Visible = false };
var xlBook =
xlApp.Workbooks.Open(System.IO.Path.Combine(
Environment.CurrentDirectory,
"Employees.xlsx"));
var xlSheet = xlBook.Sheets[1] as Excel.Worksheet;
// For process termination
var xlHwnd = new IntPtr(xlApp.Hwnd);
var xlProc = Process.GetProcesses()
.Where(p => p.MainWindowHandle == xlHwnd)
.First();
// Get Excel data: it's 2-D array with lower bounds as 1.
object[,] arr = xlSheet.Range["A1"].CurrentRegion.Value;
// Shutdown Excel
xlBook.Close();
xlApp.Quit();
xlProc.Kill();
GC.Collect();
GC.WaitForFullGCComplete();
return arr;
}
Now you can use one of the ways to generate query.
Option 1. Use ExecuteSqlCommand
When using ExecuteSqlCommand, it's advisable to use parameterized queries to avoid errors. You can pass explicitly created SqlCeParameter or just pass a value.
private void UseExecuteSqlCommand()
{
object[,] arr = GetExcelData();
using (var db = new EmpContext())
{
db.Database.Initialize(true);
int count = 0;
string sql = "INSERT INTO Venues (Name, City, Telephone) " +
"VALUES (#name, #city, #phone);";
// Start from 2-nd row since we need to skip header
for (int r = 2; r <= arr.GetUpperBound(0); ++r)
{
db.Database.ExecuteSqlCommand(
sql,
new SqlCeParameter("#name", (string)arr[r, 1]),
new SqlCeParameter("#city", (string)arr[r, 2]),
new SqlCeParameter("#phone", (string)arr[r, 3])
);
++count;
}
conn.Close();
MessageBox.Show($"{count} records were saved.");
}
}
Option 2. Use DbConnection
If you want your code to be more generic, you can create method which would accept DbConnection. This will allow to pass either SqlConnection or SqlCeConnection. But the code becomes more verbose because we can't use constructors since these classes are abstract.
private void UseDbConnection()
{
object[,] arr = GetExcelData();
using (var db = new EmpContext())
{
db.Database.Initialize(true);
int count = 0;
string sql = "INSERT INTO Venues (Name, City, Telephone) " +
"VALUES (#name, #city, #phone);";
DbParameter param = null;
DbConnection conn = db.Database.Connection;
conn.Open();
DbCommand command = conn.CreateCommand();
command.CommandText = sql;
command.CommandType = CommandType.Text;
// Create parameters
// Name
param = command.CreateParameter();
param.ParameterName = "#name";
command.Parameters.Add(param);
// City
param = command.CreateParameter();
param.ParameterName = "#city";
command.Parameters.Add(param);
// Telephone
param = command.CreateParameter();
param.ParameterName = "#phone";
command.Parameters.Add(param);
// Start from 2-nd row since we need to skip header
for (int r = 2; r <= arr.GetUpperBound(0); ++r)
{
command.Parameters["#name"].Value = (string)arr[r, 1];
command.Parameters["#city"].Value = (string)arr[r, 2];
command.Parameters["#phone"].Value = (string)arr[r, 3];
command.ExecuteNonQuery();
++count;
}
conn.Close();
MessageBox.Show($"{count} records were saved.");
}
}
You can also use ordinal positions for parameters which eliminates creating parameters names and makes code much shorter:
private void UseDbConnection()
{
object[,] arr = GetExcelData();
using (var db = new EmpContext())
{
db.Database.Initialize(true);
int count = 0;
// Take a note - use '?' as parameters
string sql = "INSERT INTO Venues (Name, City, Telephone) " +
"VALUES (?, ?, ?);";
DbConnection conn = db.Database.Connection;
conn.Open();
DbCommand command = conn.CreateCommand();
command.CommandText = sql;
command.CommandType = CommandType.Text;
// Create parameters
command.Parameters.Add(command.CreateParameter());
command.Parameters.Add(command.CreateParameter());
command.Parameters.Add(command.CreateParameter());
for (int r = 2; r <= arr.GetUpperBound(0); ++r)
{
// Access parameters by position
command.Parameters[0].Value = (string)arr[r, 1];
command.Parameters[1].Value = (string)arr[r, 2];
command.Parameters[2].Value = (string)arr[r, 3];
command.ExecuteNonQuery();
++count;
}
conn.Close();
MessageBox.Show($"{count} records were saved.");
}
}
P.S.
I didn't check whether the underlying connection is opened, but it's a good idea to do so.
Based on JohnyL's excellent input, I was able to modify my code so that it works with either SQL Server Express and and SQL Server CE. I'll put my new code as an answer, as I had to parameterize it further, as I couldn't write the property names in the code either. But this was a simple step, once I got the idea from JohnyL. Not sure though, if the database writing operation should be wrapped inside a DbTransaction, but this worked for now.
public static class ExcelUploader
{
static ArrayList data;
static List<string> tableNames;
static List<DbCommand> cmdList = new List<DbCommand>();
static DbConnection conn;
public static void Upload(string filePath)
{
data = new ArrayList();
tableNames = new List<string>();
//get Excel data to array list
ArrayList upLoadData = ReadFile(filePath);
using (var db = new DataContext())
{
conn = db.Database.Connection;
//transform arraylist into a list of DbCommands
ArrayListToCommandList(upLoadData);
conn.Open();
try
{
foreach (var cmd in cmdList)
{
//Console.WriteLine(cmd.CommandText);
cmd.ExecuteNonQuery();
}
}
catch (Exception e)
{
var result = e.Message;
MessageBox.Show(result);
}
}
}
//opens Excel file and reads worksheets to arraylist
private static ArrayList ReadFile(string fileName)
{
List<string> commands = new List<string>();
var xlApp = new Microsoft.Office.Interop.Excel.Application();
var wb = xlApp.Workbooks.Open(fileName, ReadOnly: true);
xlApp.Visible = false;
foreach (Worksheet ws in wb.Worksheets)
{
var r = ws.UsedRange;
var array = r.Value;
data.Add(array);
tableNames.Add(ws.Name);
}
wb.Close(SaveChanges: false);
xlApp.Quit();
return data;
}
//transforms arraylist to a list of DbCommands
private static void ArrayListToCommandList(ArrayList arrList)
{
List<DbCommand> result = new List<DbCommand>();
for (int tableAmount = 0; tableAmount < data.Count; tableAmount++)
{
ArrayToSqlCommands(arrList[tableAmount] as Array, tableNames[tableAmount]);
}
}
private static void ArrayToSqlCommands(Array arr, string tableName)
{
//Excel row which holds property names
int propertyRow = 1;
//First Excel row with values
int firstDataRow = 2;
string sql = "";
DbCommand cmd = conn.CreateCommand();
sql += "INSERT INTO " + tableName + "(";
//add column names to command text
for (int colIndex = 1; colIndex <= arr.GetUpperBound(1); colIndex++)
{
//get property name
sql += arr.GetValue(propertyRow, colIndex);
//add comma if not last column, otherwise close bracket
if (colIndex == arr.GetUpperBound(1))
{
sql += ") Values (";
}
else
{
sql += ", ";
}
}
//add value parameter names to command text
for (int colIndex = 1; colIndex <= arr.GetUpperBound(1); colIndex++)
{
//get property name
sql += "#" + arr.GetValue(propertyRow, colIndex);
//add comma if not last column, otherwise close bracket
if (colIndex == arr.GetUpperBound(1))
{
sql += ");";
}
else
{
sql += ", ";
}
}
//add data elements as command parameter values
for (int rowIndex = firstDataRow; rowIndex <= arr.GetUpperBound(0); rowIndex++)
{
//initialize command
cmd = conn.CreateCommand();
cmd.CommandText = sql;
cmd.CommandType = CommandType.Text;
for (int colIndex = 1; colIndex <= arr.GetUpperBound(1); colIndex++)
{
//set parameter values
DbParameter param = null;
param = cmd.CreateParameter();
param.ParameterName = "#" + (string)arr.GetValue(propertyRow, colIndex);
cmd.Parameters.Add(param);
cmd.Parameters[param.ParameterName].Value = arr.GetValue(rowIndex, colIndex);
}
//add command to command list
cmdList.Add(cmd);
}
}
}

Copying large datatable into excel in ASP.Net

I've written some code that copies the data from an Azure database into an Excel file. This can be found at the end of this question.
The problem is it takes forever to populate an excel sheet when I have 10k rows for one of the tables. Obviously, this is not ideal for Excel but at this point it has to be done this way. I'm wondering if there is a faster way to code this.
Certainly, creating excel sheet is the bottleneck, because C# grabs the dataset in seconds. If I go into Excel and view the data and then right click and copy with headers and paste that into and excel sheet it also does this in seconds.
So can I programmatically do this?
private void createExcelFile()
{
string fileName = "FvGReport.xlsx";
string filePath = HttpContext.Current.Request.MapPath("~/App_Data/" + fileName); //check www.dotnetperls.com/mappath
string sqlQuery = "";
List<string> sheetNames = new List<string>();
foreach (ListItem item in ddlSummary_Supplier.Items)
{
string sqlSummary = "SELECT * FROM FvGSummaryAll WHERE Supplier_Code = '" + item.Text + "'; ";
sqlQuery = sqlQuery + sqlSummary;
sheetNames.Add("Summary " + item.Text);
string sqlPaymentsSummary = "SELECT * FROM FvGSummaryPayment WHERE Supplier_Code = '" + item.Text + "'; ";
sqlQuery = sqlQuery + sqlPaymentsSummary;
sheetNames.Add("PaymentSummary " + item.Text);
}
DataSet dataSet = new DataSet();
//string sqlQuery = #"SELECT * FROM FvGData WHERE Supplier_Code = 'SFF Pacific'; SELECT * FROM FvGSummaryPayment";
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.SelectCommand = new SqlCommand(sqlQuery, connection);
adapter.Fill(dataSet);
}
//this reference conflicts with System.Data as both have DataTable. So defining it here.
Microsoft.Office.Interop.Excel.Application ExcelApp = new Microsoft.Office.Interop.Excel.Application();
Microsoft.Office.Interop.Excel.Workbook excelWorkBook = null;
Microsoft.Office.Interop.Excel.Worksheet excelWorkSheet = null;
ExcelApp.Visible = true;
excelWorkBook = ExcelApp.Workbooks.Add(Microsoft.Office.Interop.Excel.XlWBATemplate.xlWBATWorksheet);
//excel rows start at 1 not 0
try
{
for (int i = 1; i < dataSet.Tables.Count; i++)
{
excelWorkBook.Worksheets.Add(); //Adds new sheet in Excel WorkBook
}
for (int i = 0; i < dataSet.Tables.Count; i++)
{
int dsRow = 1;
excelWorkSheet = excelWorkBook.Worksheets[i + 1];
//Writing Columns Name in Excel Sheet
for (int col = 1; col < dataSet.Tables[i].Columns.Count; col++)
{
excelWorkSheet.Cells[dsRow, col] = dataSet.Tables[i].Columns[col - 1].ColumnName;
}
dsRow++;
for (int xlRow = 0; xlRow < dataSet.Tables[i].Rows.Count; xlRow++)
{
//Excel row and col positions for writing row = 1, col = 1
for (int col = 1; col < dataSet.Tables[i].Columns.Count; col++)
{
excelWorkSheet.Cells[dsRow, col] = dataSet.Tables[i].Rows[xlRow][col - 1].ToString();
}
dsRow++;
}
excelWorkSheet.Name = sheetNames[i]; //Renaming ExcelSheets
}
excelWorkBook.SaveAs(filePath);
excelWorkBook.Close();
ExcelApp.Quit();
Marshal.ReleaseComObject(excelWorkSheet);
Marshal.ReleaseComObject(excelWorkBook);
Marshal.ReleaseComObject(ExcelApp);
}
catch (Exception ex)
{
lblNoData.Text = ex.ToString();
}
finally
{
foreach (Process process in Process.GetProcessesByName("Excel"))
{
process.Kill();
}
}
downloadExcel(filePath, fileName);
}
It looks like you're using Office Automation, which is usually slow on things like this, in my experience. I would suggest saving the output as a delimited file (.csv) and using automation to open that file (or files) with Excel and then save it as a spreadsheet.
I would suggest you to try using some ETL tool, Esspecially if you will do this from time to time again. If you take Talend, for instance, .... you connect to the DB and the schema will be pulled on its own. Take a SQL input component and connect it to a Excel component and you are done. Take about 5 minutes, without a single line of code
I'm not sure what you mean by 'forever', but for comparison I have a process that writes an OpenXML Spreadsheet of 46,124 row with ~500 characters per row in less than 17-seconds. This is generated on by a C# process that is on a separate server at the same hosting facility as the database server.
If writing to a CSV is an option, then that is going to be the solution with the best performance. OpenXML will give you the next best performance, I found the following article to be the most helpful when I was trying to put together my process:
Read-and-Write-Microsoft-Excel-with-Open-XML-SDK
Regarding memory -- You have two things you need to put in memory, your incoming data and your outgoing file. Regardless of what type of file you write, you'll want to use a SqlDataReader instead of your dataSet. This means your incoming data will only have one row at a time in memory (instead of all 10K). When writing your file (CSV or OpenXML) if you write directly to disk (FileStream) instead of memory (MemoryStream) you'll only have that little bit in memory.
Especially if you are running code that is running within your website, you don't want to use up a bunch of memory at once because .NET/IIS doesn't handle that very well.

Export Table from SQL Server to Excel 2007 using C#

my table in sql looks like this:
CREATE TABLE InjuryScenario
(
InjuryScenario_id int identity(1,1),
InjuryDay int,
InjuryMonth int,
InjuryYear int,
InjuryDesc varchar(80),
InjuryComments varchar(50),
AlmostInjury int,
InjuryInSchool varchar(20),
ProductInjury varchar(20),
Cause_id int,
CauseType_id int,
CauseChar_id int,
Place_id int,
PlaceType_id int,
InjuryDate_id int,
Username varchar(50),
TimeStamp datetime default (getdate()),
constraint pk_InjuryScenario_id primary key (InjuryScenario_id),
constraint fk_Cause_InjuryScenario foreign key(Cause_id) references Cause(Cause_id) on delete cascade,
constraint fk_CauseType_InjuryScenario foreign key(CauseType_id) references CauseType(CauseType_id) on delete no action,
constraint fk_CauseChar_InjuryScenario foreign key(CauseChar_id) references CauseChar(CauseChar_id) on delete no action,
constraint fk_Place_InjuryScenario foreign key(Place_id) references Place(Place_id) on delete cascade,
constraint fk_PlaceType_InjuryScenario foreign key(PlaceType_id) references PlaceType(PlaceType_id) on delete no action,
constraint fk_InjuryDate_InjuryScenario foreign key(InjuryDate_id) references InjuryDate(InjuryDate_id) on delete cascade,
constraint fk_Users_InjuryScenario foreign key(Username) references Users(Username) on delete cascade
)
I fill this table with data and i want to show this table in Excel 2007.
how can i do it using c#?
Thank you!
protected void insertBTN(object sender, EventArgs e)
{string conString = #"Data Source =XXXX; Initial Catalog=XXXX; Persist Security Info=True;User ID=XXXX; Password=XXXX";SqlConnection sqlCon = new SqlConnection(conString);
sqlCon.Open();
SqlDataAdapter da = new SqlDataAdapter("SELECT * from InjuryScenario", sqlCon);
System.Data.DataTable dtMainSQLData = new System.Data.DataTable();
da.Fill(dtMainSQLData);
DataColumnCollection dcCollection = dtMainSQLData.Columns;
// Export Data into EXCEL Sheet
Microsoft.Office.Interop.Excel.ApplicationClass ExcelApp = new
Microsoft.Office.Interop.Excel.ApplicationClass();
ExcelApp.Application.Workbooks.Add(Type.Missing);
// ExcelApp.Cells.CopyFromRecordset(objRS);
for (int i = 1; i < dtMainSQLData.Rows.Count + 2; i++)
{
for (int j = 1; j < dtMainSQLData.Columns.Count + 1; j++)
{
if (i == 1)
{
ExcelApp.Cells[i, j] = dcCollection[j - 1].ToString();
}
else
ExcelApp.Cells[i, j] = dtMainSQLData.Rows[i - 2][j - 1].ToString();
}
}
ExcelApp.ActiveWorkbook.SaveCopyAs("C:\\Users\\Mor Shivek\\Desktop\\test.xls");
ExcelApp.ActiveWorkbook.Saved = true;
ExcelApp.Quit();}
This is hundred percent working for VS-2013 Premium for C#. just copy
and paste the code That's it. Its working for SQL Server database You
can save the data into xls, xlsx, or csv and can use that same csv
for parameterization. you need to install following packages to make
it work.
using System.Data.SqlClient
using Excel=Microsoft.Office.Interop.Excel
using SQL = System.Data
///***Copy from here////
SqlConnection cnn;
string connectionstring = null;
string sql = null;
string data = null;
int i = 0;
int j = 0;
////*** Preparing excel Application
Excel.Application xlApp;
Excel.Workbook xlWorkBook;
Excel.Worksheet xlWorkSheet;
object misValue = System.Reflection.Missing.Value;
///*** Opening Excel application
xlApp = new Microsoft.Office.Interop.Excel.Application();
xlWorkBook = xlApp.Workbooks.Open(#"Fullpath\Book1.csv");
xlWorkSheet = (Excel.Worksheet)(xlWorkBook.ActiveSheet as Excel.Worksheet);
////*** It will always remove the prvious result from the CSV file so that we can get always the updated data
xlWorkSheet.UsedRange.Select();
xlWorkSheet.UsedRange.Delete(Excel.XlDeleteShiftDirection.xlShiftUp);
xlApp.DisplayAlerts = false;
//xlWorkBook.Save();
/////***Opening SQL Database using Windows Authentication
connectionstring = "Integrated Security = SSPI;Initial Catalog=DatabaseName; Data Source=ServerName;";
cnn = new SqlConnection(connectionstring);
cnn.Open();
////** Write your Sql Query here
sql = "SELECT TOP 10 [FirstName],[MiddleName],[LastName],[Email],[AltEmail],[AuthorizedUserName] From Tablename";
///*** Preparing to retrieve value from the database
SQL.DataTable dtable = new SQL.DataTable();
SqlDataAdapter dscmd = new SqlDataAdapter(sql, cnn);
SQL.DataSet ds = new SQL.DataSet();
dscmd.Fill(dtable);
////*** Generating the column Names here
string[] colNames = new string[dtable.Columns.Count];
int col = 0;
foreach (SQL.DataColumn dc in dtable.Columns)
colNames[col++] = dc.ColumnName;
char lastColumn = (char)(65 + dtable.Columns.Count - 1);
xlWorkSheet.get_Range("A1", lastColumn + "1").Value2 = colNames;
xlWorkSheet.get_Range("A1", lastColumn + "1").Font.Bold = true;
xlWorkSheet.get_Range("A1", lastColumn + "1").VerticalAlignment
= Excel.XlVAlign.xlVAlignCenter;
/////*** Inserting the Column and Values into Excel file
for (i = 0 ; i <= dtable.Rows.Count - 1; i++)
{
for (j = 0; j <= dtable.Columns.Count-1; j++)
{
data = dtable.Rows[i].ItemArray[j].ToString();
xlWorkSheet.Cells[i + 2, j + 1] = data;
}
}
///**Saving the csv file without notification.
xlApp.DisplayAlerts = false;
xlWorkBook.Save();
xlWorkBook.Close(true, misValue, misValue);
xlApp.Quit();
releaseObject(xlWorkSheet);
releaseObject(xlWorkBook);
releaseObject(xlApp);
}
private void releaseObject(object obj)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
obj = null;
}
catch (Exception ex)
{
obj = null;
MessageBox.Show("Exception Occured while releasing object " + ex.ToString());
}
finally
{
GC.Collect();
}
}
I tried this code it's working.
private void button1_Click(object sender, EventArgs e)
{
SqlConnection cnn;
string connectionstring = null;
string sql = null;
string data = null;
int i = 0;
int j = 0;
Excel.Application xlApp;
Excel.Workbook xlWorkBook;
Excel.Worksheet xlWorkSheet;
object misValue = System.Reflection.Missing.Value;
xlApp = new Microsoft.Office.Interop.Excel.Application();
xlWorkBook = xlApp.Workbooks.Add(misValue);
xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
connectionstring ="Data Source=IN-WTS-SAM;Initial Catalog=MSNETDB;Integrated Security=True;Pooling=False";
cnn = new SqlConnection(connectionstring);
cnn.Open();
sql = "SELECT * FROM Emp";
SqlDataAdapter dscmd = new SqlDataAdapter(sql, cnn);
DataSet ds = new DataSet();
dscmd.Fill(ds);
for (i = 0; i <= ds.Tables[0].Rows.Count - 1; i++)
{
for (j = 0; j <= ds.Tables[0].Columns.Count - 1; j++)
{
data = ds.Tables[0].Rows[i].ItemArray[j].ToString();
xlWorkSheet.Cells[i + 1, j + 1] = data;
}
}
xlWorkBook.SaveAs("informations.xls", Excel.XlFileFormat.xlWorkbookNormal, misValue, misValue, misValue, misValue, Excel.XlSaveAsAccessMode.xlExclusive, misValue, misValue, misValue, misValue, misValue);
xlWorkBook.Close(true, misValue, misValue);
xlApp.Quit();
releaseObject(xlWorkSheet);
releaseObject(xlWorkBook);
releaseObject(xlApp);
MessageBox.Show("Excel file created , you can find the file D:\\Sam-informations.xls");
}
private void releaseObject(object obj)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
obj = null;
}
catch (Exception ex)
{
obj = null;
MessageBox.Show("Exception Occured while releasing object " + ex.ToString());
}
finally
{
GC.Collect();
}
}
}
Try this if it works for you.
Export sql table to excel with column name
for (i = 0; i <= ds.Tables[0].Columns.Count - 1; i++)
{
data = ds.Tables[0].Columns[i].ColumnName.ToString();
xlWorkSheet.Cells[1, i + 1] = data;
}

Excel DNA - Argument works from spraedsheet, but not from vba

This is a bit weird to me.
I have the following code:
public static object[,] dbMultipleSingleQueries(string strDBPath, object[] oSQL)
{
//This is needed for attaching a DB.
object[,] objOut;
objOut = new Object[1, 1];
try
{
//Prepare DB connection string
strDBPath = "Data Source=" +
#strDBPath +
";Version=3";
//SQL Objs
SQLiteConnection dbConnection = new SQLiteConnection(strDBPath);
SQLiteCommand dbCommand = null;
SQLiteDataAdapter dbAdapter = null;
DataTable table = new DataTable();
//For looping
int j = 0;
//Conn str
dbConnection.Open();
dbCommand = new SQLiteCommand(dbConnection);
while (j < oSQL.Length)
{
try
{
dbCommand.CommandText = oSQL[j].ToString();
dbCommand.CommandType = System.Data.CommandType.Text;
if (j == (oSQL.Length - 1))
{
dbAdapter = new SQLiteDataAdapter(dbCommand);
dbAdapter.Fill(table);
objOut = table2Array(table);
}
else
{
dbCommand.ExecuteNonQuery();
objOut[0, 0] = "Success!";
}
}
catch (Exception e2)
{
System.Console.WriteLine(e2.StackTrace.ToString());
objOut[0, 0] = e2.StackTrace.ToString();
}
j++;
};
dbConnection.Close();
dbConnection = null;
}
catch (Exception e)
{
objOut[0, 0] = e.StackTrace.ToString();
}
return objOut;
}
which works perfectly if i feed my arguments from a spreadsheet like this:
o = Application.Run("dbMultipleSingleQueries", "20130201.db", [test])
where [test] is a range in my workbook with queries that work and look something like this:
attach database 'a.db' as d1
select * from d1.main
now if i do the following in vba
Option Explicit
Sub Main()
Dim sql(1 To 2, 1 To 1)
sql(1, 1) = "attach database 'a.db' as d1"
sql(2, 1) = "select * from d1.main"
o = Application.Run("dbMultipleSingleQueries", "20130201.db", sql)
End Sub
this does not work. i get a "Type Mismatch" error. Any input why would be a huge help this is driving me nuts!
Your function prototype specifies that the second argument needs to be of type "Object[]" - but you are passing a variant that probably thinks it is a string array, based on the assignment.

Import Excel file into Microsoft SQL Server using C#

I have a C# program that I want to import an Excel file into Microsoft SQL Server 2008 R2.
Can someone give me a link or tutorial where I can fully understand on how to this?
I've been doing this for a long time and I really don't have an idea on how to implement this.
Please help. Thanks.
string excelConnectionString = #"Provider=Microsoft
.Jet.OLEDB.4.0;Data Source=Book1.xls;Extended
Properties=""Excel 8.0;HDR=YES;""";
// Create Connection to Excel Workbook
We can Import excel to sql server like this
using (OleDbConnection connection =
new OleDbConnection(excelConnectionString)){
OleDbCommand command = new OleDbCommand
("Select ID,Data FROM [Data$]", connection);
connection.Open();
`// Create DbDataReader to Data Worksheet `
using (DbDataReader dr = command.ExecuteReader())
{
// SQL Server Connection String
string sqlConnectionString = "Data Source=.;
Initial Catalog=Test;Integrated Security=True";
// Bulk Copy to SQL Server
using (SqlBulkCopy bulkCopy =
new SqlBulkCopy(sqlConnectionString))
{
bulkCopy.DestinationTableName = "ExcelData";
bulkCopy.WriteToServer(dr);
}
This code may also help you.
private void processExcel(string filename)
{
filename = Server.MapPath("~/Files/WM-0b23-productsBook.xlsx");
Microsoft.Office.Interop.Excel.Application xlApp;
Microsoft.Office.Interop.Excel.Workbook xlWorkBook;
Microsoft.Office.Interop.Excel.Worksheet xlWorkSheet;
var missing = System.Reflection.Missing.Value;
xlApp = new Microsoft.Office.Interop.Excel.Application();
xlWorkBook = xlApp.Workbooks.Open(filename, false, true, missing, missing, missing, true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, '\t', false, false, 0, false, true, 0);
xlWorkSheet = (Microsoft.Office.Interop.Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);
Microsoft.Office.Interop.Excel.Range xlRange = xlWorkSheet.UsedRange;
Array myValues = (Array)xlRange.Cells.Value2;
int vertical = myValues.GetLength(0);
int horizontal = myValues.GetLength(1);
System.Data.DataTable dt = new System.Data.DataTable();
// must start with index = 1
// get header information
for (int i = 1; i <= horizontal; i++)
{
dt.Columns.Add(new DataColumn(myValues.GetValue(1, i).ToString()));
}
// Get the row information
for (int a = 2; a <= vertical; a++)
{
object[] poop = new object[horizontal];
for (int b = 1; b <= horizontal; b++)
{
poop[b - 1] = myValues.GetValue(a, b);
}
DataRow row = dt.NewRow();
row.ItemArray = poop;
dt.Rows.Add(row);
}
// assign table to default data grid view
GridView1.DataSource = dt;
GridView1.DataBind();
xlWorkBook.Close(true, missing, missing);
xlApp.Quit();
releaseObject(xlWorkSheet);
releaseObject(xlWorkBook);
releaseObject(xlApp);
}
private void releaseObject(object obj)
{
try
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
obj = null;
}
catch (Exception ex)
{
obj = null;
MessageBox.Show("Unable to release the Object " + ex.ToString());
}
finally
{
GC.Collect();
}
}
Check out this post :
Bulk Insertion of Data Using C# DataTable and SQL server OpenXML function
Import Data from Excel to SQL Server
I think that most useful answers already have been given.
I am not going to describe the C# way, but rathher give you an easy way of doing it without C#:
Open your MS Access, link your MS SQL database via ODBC, open your table in Access and paste your Excel records there via copy-paste (preformatted to match the table schema.)
This way is very fast and useful when you do some one-time data import.

Categories

Resources