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.
Related
I am trying to grab cells in XLS spreadsheets, assign them to string arrays, then manipulate the data and export to multiple CVS files.
The trouble is the XLS spreadsheet contains information that is not relevant, useable data doesn't start till row 17 and columns have no headings with just the default Sheet1.
I have looked at related questions and tried figuring it out myself with no success. The following code to read the XLS kinda works but is messy to work with as the row lengths vary from one XLS file to another and it is automatically pulling empty columns and rows.
CODE
public static void xlsReader()
{
string fileName = string.Format("{0}\\LoadsAvailable.xls", Directory.GetCurrentDirectory());
string connectionString = #"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + fileName + ";" + #"Extended Properties='Excel 8.0;HDR=Yes;'";
string queryString = "SELECT * FROM [Sheet1$]";
using (OleDbConnection connection = new OleDbConnection(connectionString))
{
OleDbCommand command = new OleDbCommand(queryString, connection);
connection.Open();
OleDbDataReader reader = command.ExecuteReader();
int counter = 0;
while (reader.Read())
{
Console.WriteLine("Line " + counter + ":" + reader[28].ToString()); // Just for testing
counter++;
}
}
}
I could do a bunch of trickery with loops to get the data that is required but there has to be a query string that could get the data from row 17 with only 8 columns(not 26 columns with 18 empty)?
I have tried many query string examples and can not seam to get any to work with a starting row index or filter out the empty data.
Here is a handy method that converts an excel file to a flat file.
You may want to change the connection string properties to suit your needs. I needed headers for my case.
Note you will need the Access database engine installed on your machine. I needed the 32 bit version since the app i dev'd was 32 bit. I bet you will also need it.
I parameterized the delimiter for the flat file, because I had cases where I didn't need a comma but a pipe symbol.
How to call method ex: ConvertExcelToFlatFile(openFileName, savePath, '|'); // pipe delimited
// Converts Excel To Flat file
private void ConvertExcelToFlatFile(string excelFilePath, string csvOutputFile, char delimeter, int worksheetNumber = 1)
{
if (!File.Exists(excelFilePath)) throw new FileNotFoundException(excelFilePath);
if (File.Exists(csvOutputFile)) throw new ArgumentException("File exists: " + csvOutputFile);
// connection string
var cnnStr = String.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0 Xml; IMEX=1; HDR=NO\"", excelFilePath);
var cnn = new OleDbConnection(cnnStr);
// get schema, then data
var dt = new DataTable();
try
{
cnn.Open();
var schemaTable = cnn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
if (schemaTable.Rows.Count < worksheetNumber) throw new ArgumentException("The worksheet number provided cannot be found in the spreadsheet");
string worksheet = schemaTable.Rows[worksheetNumber - 1]["table_name"].ToString().Replace("'", "");
string sql = String.Format("select * from [{0}]", worksheet);
var da = new OleDbDataAdapter(sql, cnn);
da.Fill(dt);
}
catch (Exception e)
{
throw e;
}
finally
{
// free resources
cnn.Close();
}
// write out CSV data
using (var wtr = new StreamWriter(csvOutputFile)) // disposes file handle when done
{
foreach (DataRow row in dt.Rows)
{
//MessageBox.Show(row.ItemArray.ToString());
bool firstLine = true;
foreach (DataColumn col in dt.Columns)
{
// skip the first line the initial
if (!firstLine)
{
wtr.Write(delimeter);
}
else
{
firstLine = false;
}
var data = row[col.ColumnName].ToString();//.Replace("\"", "\"\""); // replace " with ""
wtr.Write(String.Format("{0}", data));
}
wtr.WriteLine();
}
}
}
I am using Asp.net with C#. I need to import data from an Excel sheet to a DataTable. The sheet has 100,000 records with four columns: Firstname, LastName, Email,Phone no.
How can I do this?
Use the following code:
public static DataTable exceldata(string filePath)
{
DataTable dtexcel = new DataTable();
bool hasHeaders = false;
string HDR = hasHeaders ? "Yes" : "No";
string strConn;
if (filePath.Substring(filePath.LastIndexOf('.')).ToLower() == ".xlsx")
strConn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filePath + ";Extended Properties=\"Excel 12.0;HDR=" + HDR + ";IMEX=0\"";
else
strConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + filePath + ";Extended Properties=\"Excel 8.0;HDR=" + HDR + ";IMEX=0\"";
OleDbConnection conn = new OleDbConnection(strConn);
conn.Open();
DataTable schemaTable = conn.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "TABLE" });
//Looping Total Sheet of Xl File
/*foreach (DataRow schemaRow in schemaTable.Rows)
{
}*/
//Looping a first Sheet of Xl File
DataRow schemaRow = schemaTable.Rows[0];
string sheet = schemaRow["TABLE_NAME"].ToString();
if (!sheet.EndsWith("_"))
{
string query = "SELECT * FROM [" + sheet3 + "]";
OleDbDataAdapter daexcel = new OleDbDataAdapter(query, conn);
dtexcel.Locale = CultureInfo.CurrentCulture;
daexcel.Fill(dtexcel);
}
conn.Close();
return dtexcel;
}
Source: http://www.codeproject.com/Questions/445400/Read-Excel-Sheet-Data-into-DataTable
You may also refer the following question: Importing Excel into a DataTable Quickly if you wish to import faster.
I'm not sure if this will work in ASP.NET but it works in WPF so maybe there's something you can take from it?
Anyway, at the global scope:
Microsoft.Office.Interop.Excel.Application xls;
Then to select and read a spreadsheet:
private void readSheet()
{
// Initialise and open file picker
OpenFileDialog openfile = new OpenFileDialog();
openfile.DefaultExt = ".xlsx";
openfile.Filter = "Office Files | *xls;.xlsx";
var browsefile = openfile.ShowDialog();
if (browsefile == true)
{
string path = openfile.FileName;
xls = new Microsoft.Office.Interop.Excel.Application();
// Dynamic File Using Uploader... Note the readOnly flag is true
Workbook excelBook = xls.Workbooks.Open(path, 0, true, 5, "", "", true, XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
Worksheet excelSheet = (Worksheet)excelBook.Worksheets.get_Item(1); ;
Range excelRange = excelSheet.UsedRange;
// Make default cell contents
string strCellData = String.Empty;
double douCellData;
// Initialise row and column
int rowCnt, colCnt = 0;
// Initialise DataTable
System.Data.DataTable dt = new System.Data.DataTable();
// Loop through first row of columns to make header
for (colCnt = 1; colCnt <= excelRange.Columns.Count; colCnt++)
{
string strColumn = "";
strColumn = Convert.ToString((excelRange.Cells[1, colCnt] as Range).Value2);
var Column = dt.Columns.Add();
Column.DataType = Type.GetType("System.String");
// Check & rename for duplicate entries
if (dt.Columns.Contains(strColumn))
Column.ColumnName = (strColumn + ", " + colCnt);
else
Column.ColumnName = strColumn;
}
dt.AcceptChanges();
// Fill in the rest of the cells
for (rowCnt = 2; rowCnt <= excelRange.Rows.Count; rowCnt++)
{
string strData = "";
for (colCnt = 1; colCnt <= excelRange.Columns.Count; colCnt++)
{
try
{
strCellData = Convert.ToString((excelRange.Cells[rowCnt, colCnt] as Range).Value2);
strData += strCellData + "|";
}
catch (Exception ex)
{
douCellData = (excelRange.Cells[rowCnt, colCnt] as Range).Value2;
strData += douCellData.ToString() + "|";
Console.Write(ex.ToString());
}
}
strData = strData.Remove(strData.Length - 1, 1);
dt.Rows.Add(strData.Split('|'));
}
dtGrid.ItemsSource = dt.DefaultView;
try
{
excelBook.Close(true, null, null);
}
catch (System.Runtime.InteropServices.COMException comEX)
{
Console.Write("COM Exception: " + comEX.ToString());
}
xls.Quit();
}
}
Speed Problems?
I'll note several ways to do this:
ODBC (answered here)
Interop (answered here)
These have drawbacks, they might not be fast; Interop requires excel, runs it, and can cause lots of problems with re-running it or the web server trying to run it.
please try #milan_m solution first. If it has problems come back here.
So some faster, potentially better solutions are as such.
NPOI
Save-As CSV
NPOI is available as a NuGet for C# and will read excel files very well. Yes this is a product recommendation, but you didn't ask for one. It is a well-maintained project and will be relevant for readers into the 2030s.
https://github.com/nissl-lab/npoi
You'll want to use NPOI if ODBC is too slow, and your users are uploading a different XLSX file as part of the use case. Unless there are only 2 or 3 internal power users you are in contact with, then you can require them to upload it as CSV.
What if the use case is: You just use one .XLSX file that's the same for all users, you deploy it with the app?
You didn't mention if this is the case or not and it makes a HUGE difference. you definitely will be miles ahead if you save as csv and consume that from the startup of the program. Or, if you need it in a datatable, import it to the data table at dev time and save it to XML file using a method on the dataset object (you have to put the tbl into a set to save as XML I believe ... many examples abound).
If you need to do super flexible lookups, a datatable (or object collection and linq-to-objects) is good.
However if you have to look up items at extreme speed, and not via ranges but just by exact match, load it to a dictionary or dictionaries at startup time, from a CSV or similar.
I did this for a power user who was searching a spreadsheet of about 2-3lakh / records with interop+excel ... operation went from 90 minutes to 30 seconds. Literally. Dictionary is about the fastest way to look stuff up in .Net, if you can fit that stuff in memory, that is, and don't have to reload different data all the time (so keep your RDBMS).
Oops.
Just now saw this question is 7 years old. ##$^##!!!
I need to read and write dbf files. Microsoft dropped support for this in Office 2013.
I am trying to use OLEDB. The problem I am having at this point (have not gone into the writing) is that when I read the header info I am having it returned sorted alphabetically while the rows are not sorted like that. I need to have them saved in the same order so that I can go back and create a dbf files (after some data processing) to pass back to my legacy applications which require DB.
This is for internal distribution so I can take care of having the correct .NET library if necessary. I can't find any reference that solves this problem and I rather use a .NET technology than writing dbf from scratch...
I apologize if this ha been answered before is this is the case I'd appreciate being pointed to the correct way of accomplishing this task.
Code to follow
if (intype == 6) //dbase
{
int rowCount = 0;
int colCount = 0;
string npath = filein;
i = npath.LastIndexOf("\\");
aux2 = MySubStr(npath, i + 1, 1); // directory --- internal library
auxstr = MySubStr(npath, i + 1, 2);
i = auxstr.IndexOf(".");
if (i > -1)
auxstr = MySubStr(auxstr, i, 1);
DataSet ds = new DataSet();
OleDbConnection connection;
string connstr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source = '"; // I can change this to proper library
connstr = string.Concat(connstr, aux2);
connstr = string.Concat(connstr, "';");
connstr = string.Concat(connstr, "Extended Properties='dBASE 5.0';"); // again not worried about DBASE 3 or 4
connection = new OleDbConnection(connstr);
try
{
connection.Open();
DataTable dbSchema = connection.GetOleDbSchemaTable(OleDbSchemaGuid.Columns, new object[] { null, null, auxstr, null });
i = 0;
foreach (DataRow rownm in dbSchema.Rows)
{
header[i] = rownm["COLUMN_NAME"].ToString();
myDT[i++] = rownm["DATA_TYPE"].ToString(); // not using at this point... will be used when writing dbfs
}
colCount = i;
aux2 = "Select * from ";
aux2 = string.Concat(aux2, auxstr);
OleDbDataAdapter da = new OleDbDataAdapter(aux2, connection);
da.Fill(ds,"ZDATA");
count = ds.Tables[0].Rows.Count;
}
catch (Exception e)
{
auxstr = e.ToString();
messages[cntmsgs++] = auxstr;
zcode = 99;
logerror(messages, cntmsgs, zcode);
return 99;
}
System.Data.OleDb.OleDbCommand cmd1 = new System.Data.OleDb.OleDbCommand(aux2, connection);
OleDbDataReader reader;
reader = cmd1.ExecuteReader();
StreamWriter ftmp = new StreamWriter(fileout[0], false, System.Text.Encoding.Default);
while (reader.Read())
{
if (rowCount == 0)
{
auxstr ="";
for (i = 0; i < colCount -1; i++)
{
auxstr += "\"" + header[i] + "\",";
}
auxstr += "\"" + header[colCount - 1] + "\"";
ftmp.WriteLine(auxstr);
rowCount++;
}
aux2 = "";
for (i = 0; i < reader.FieldCount; i++)
{
if (reader.IsDBNull(i))
auxstr = "";
else
auxstr = reader.GetString(i);
auxstr = "\"" + auxstr + "\"";
if (i < reader.FieldCount - 1)
{
auxstr = string.Concat(auxstr, ",");
aux2 = string.Concat(aux2, auxstr);
}
else
{
aux2 = string.Concat(aux2, auxstr);
}
}
ftmp.WriteLine(aux2);
}
ftmp.Close();
reader.Close();
connection.Close();
filein = fileout[0];
intype = 2; // right now forcing additional processing in another block...
return 0;
}
You mention .dbf files, but are they really dBASE 5 or was that just a connection you were able to get. I personally have utilized Microsoft's Visual Foxpro OleDb provider. You can make a connection, query the tables, build parameterized select, insert, update, delete and don't need to really know about header and such.
There are many questions about connections to dbf tables and such, but I normally use an OleDbDataAdapter, put my command to execute in that and pull the data via .Fill() into a DataTable object.
Then I can do things like
foreach( DataColumn dc in MyTable.Columns )
[write whatever output of things like dc.ColumnName, dc.Type, etc]
foreach( DataRow dr in MyTable.Rows )
{
write out... dr["WhateverColumn"]
}
No, not exact code, but simple to work with. Building commands to push data back is easy too. But if your big deal is writing the content out... Are you really trying to rewrite the table and reconverting everything to strings?
The other comment about too much noise is accurate... In summary, yes, Microsoft dropped support for whatever, but what do you WANT to do.
I have just written what has to be considered utterly hideous code to count the rows that contain data in the worksheets called "Data" from all the spreadsheets in a given directory. Here's the code
private const string _ExcelLogDirectoryPath = #"..\..\..\..\Model\ExcelLogs\";
static void Main()
{
var excelLogPaths = Directory.GetFiles(_ExcelLogDirectoryPath, "*.xl*");
var excel = new Microsoft.Office.Interop.Excel.Application();
var excelRowCounts = new Dictionary<string, int>();
foreach (var filePath in excelLogPaths)
{
var spreadsheet = excel.Workbooks.Open(Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) + "/" + filePath);
var worksheet = spreadsheet.Sheets["Data"] as Worksheet;
if (worksheet != null)
{
// var rowCount = UsedRange.Rows.Count - 1; DOES NOT WORK, THE number is bigger than the 'real' answer
var rowCount = 0;
for (var i = 1 ; i < 1000000000; i++)
{
var cell = worksheet.Cells[i, 1].Value2; // "Value2", great name for a property, thanks guys
if (cell != null && cell.ToString() != "") // Very fragile (e.g. skipped rows will break this)
{
rowCount++;
}
else
{
break;
}
}
var name = spreadsheet.Name.Substring(spreadsheet.Name.IndexOf('p'), spreadsheet.Name.IndexOf('.') - spreadsheet.Name.IndexOf('p'));
excelRowCounts.Add(name, rowCount - 1);
}
}
I cannot believe this is the right way to do this. It is crazy slow and includes calls to properties with names like Value2 that do not feel like an intended part of a public API. But the method suggested elsewhere dramatically over reports the number of rows (with data in them).
What is the correct was to count the rows with data in them from an Excel worksheet?
========== EDIT 1 ==========
The reason that both UsedRange.Rows.Count and Sid's ACE.OLEDB solution over report the number of rows appears to be a pink background colour that is applied to some of the columns (but only extending to row 7091). Is there a simple/elegant way to count the rows with data in them (i.e. with non-null cell values) regardless of the display colour?
========== EDIT 2 ===========
Sid's ACE.OLEDB solution with the addition he suggests so that the tSQL line reads
var sql = "SELECT COUNT (*) FROM [" + sheetName + "$] WHERE NOT F1 IS NULL";
works. I'll mark that as the answer.
This should do the trick. You can call it with each filename to retrieve the number of rows.
private string GetNumberOfRows(string filename, string sheetName)
{
string connectionString;
string count = "";
if (filename.EndsWith(".xlsx"))
{
connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filename + ";Mode=ReadWrite;Extended Properties=\"Excel 12.0;HDR=NO\"";
}
else if (filename.EndsWith(".xls"))
{
connectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + filename + ";Mode=ReadWrite;Extended Properties=\"Excel 8.0;HDR=NO;\"";
}
string SQL = "SELECT COUNT (*) FROM [" + sheetName + "$]";
using (OleDbConnection conn = new OleDbConnection(connectionString))
{
conn.Open();
using (OleDbCommand cmd = new OleDbCommand(SQL, conn))
{
using (OleDbDataReader reader = cmd.ExecuteReader())
{
reader.Read();
count = reader[0].ToString();
}
}
conn.Close();
}
return count;
}
There might be an even faster way of retrieving just the row count, but I know this works.
if you use interop is why don't use UsedRange?
_Worksheet.UsedRange.Rows.Count
A project I'm working on contains an MDB (acecss database) file. I'd like to export the contents of the tables to text, but am having a hard time finding a way to do it easily using C#. Is there a faster way than using OLEDB and queries?
Update:
Ideally I'd like to not have to statically name each table (there are hundreds) and I have to use .NET 2.0 or below.
There might be a more efficient way, but you could populate the data into a DataTable, and then export to a text file:
Getting data into the DataTable:
string connString = "Provider=Microsoft.ACE.OLEDB.12.0;data source=C:\\marcelo.accdb";
DataTable results = new DataTable();
using(OleDbConnection conn = new OleDbConnection(connString))
{
OleDbCommand cmd = new OleDbCommand("SELECT * FROM Clientes", conn);
conn.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter(cmd);
adapter.Fill(results);
}
Exporting the DataTable to CSV:
EDIT I haven't tested this, but something like this should work for .NET 2.0.
//initialize the strinbuilder
StringBuilder sb = new StringBuilder();
//append the columns to the header row
string[] columns = new string[dt.Columns.Count - 1];
for (int i = 0; i < dt.Columns.Count; i++)
columns[i] = dt.Columns[i].ColumnName;
sb.AppendLine(string.Join(",", columns));
foreach (DataRow row in dt.Rows)
{
//append the data for each row in the table
string[] fields = new string[row.ItemArray.Length];
for (int x = 0; x < myDataRow.ItemArray.Length; x++)
arr[x] = row[x].ToString();
sb.AppendLine(string.Join(",", fields));
}
File.WriteAllText("test.csv", sb.ToString());
No obvious way comes to mind. Just write something that iterates through the tables and spits out the data in whatever text format you want (.csv, tab delimited, etc).
You could always write it in VBA inside of Access, but I don't know if that would make it faster or slower.
If you want to go the Interop route, you can do it in a single command with the Access TransferText method:
using Access = Microsoft.Office.Interop.Access;
using System.Runtime.InteropServices;
static void ExportToCsv(string databasePath, string tableName, string csvFile) {
Access.Application app = new Access.Application();
app.OpenCurrentDatabase(databasePath);
Access.DoCmd doCmd = app.DoCmd;
doCmd.TransferText(Access.AcTextTransferType.acExportDelim, Type.Missing, tableName, csvFile, true);
app.CloseCurrentDatabase();
Marshal.FinalReleaseComObject(doCmd);
doCmd = null;
app.Quit();
Marshal.FinalReleaseComObject(app);
app = null;
}
I do not know C#, but here is another idea, but quite rough. It uses Microsoft.Office.Interop.Access.Dao
DBEngine dbEng = new DBEngine();
Workspace ws = dbEng.CreateWorkspace("", "admin", "",
WorkspaceTypeEnum.dbUseJet);
Database db = ws.OpenDatabase("z:\\docs\\test.accdb", false, false, "");
foreach (TableDef tdf in db.TableDefs)
{
string tablename=tdf.Name;
if (tablename.Substring(0,4) != "MSys")
{
string sSQL = "SELECT * INTO [Text;FMT=Delimited;HDR=Yes;DATABASE=Z:\\Docs].[out_"
+ tablename + ".csv] FROM " + tablename;
db.Execute(sSQL);
}
}