Exception occurs reading spreadsheet after saving ExcelPackage (EPPlus) - epplus

I am manipulating data in a .xlsx spreadsheet using EPPlus (4.0.4) and saving the file, immediately afterwards I am loading parts of the data into datatables.
After I call the .Save() method the spreadsheet is saved but then I get a 'External table is not in the expected format.' exception when trying to fill a DataAdapter.
I've created a console app to minimise the amount of code required to demonstrate the issue, here is the code:
using OfficeOpenXml;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace EPPlusSaveIssue
{
class Program
{
static void Main(string[] args)
{
string pathToFile = "C:\\Users\\Peter\\Documents\\test.xlsx";
FileInfo newFile = new FileInfo(pathToFile);
using (var pck = new ExcelPackage(newFile))
{
pck.Save();
}
string connectionStringFormat = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties=\"Excel 12.0;HDR=No;IMEX=1\"";
using (OleDbConnection con = new OleDbConnection(string.Format(connectionStringFormat, pathToFile)))
{
using (OleDbDataAdapter dataAdapter = new OleDbDataAdapter("SELECT * FROM [Sheet1$] ", con))
{
DataTable dataTable = new DataTable();
dataAdapter.Fill(dataTable);
Console.WriteLine(dataTable.Rows.Count.ToString());
}
}
}
}
}
Steps to reproduce:
Run the application against a newly created spreadsheet, the exception occurs
Comment out the pck.Save(); line and the exception still occurs (the spreadsheet is in some way mangled at this point)
Open the spreadsheet in Excel and save it
Run the application again (with the pck.Save() still commented out) and it runs
Put the pck.Save(); line back in and the exception occurs.
So, there's a workaround which is impractical, i.e. do the manipulation save the file using EPPlus then save it in Excel then run the process without saving it using EPPlus.
It seems the .Save() method is putting the file in an unusual state, I don't know what else to try, any advice would be greatly appreciated.

You cannot call save() because your workbook doesn't have any worksheet bro. At least 1 sheet is required for a normal excel file.
Try to add var ws = pck.Workbook.Worksheets.Add("Sheet1"); before the save().
I used EPPlus long time ago. I don't know why the Document is removed but the dev team may put it back any time, below link.
http://epplus.codeplex.com/documentation

I just revisited this issue and found that EPPlus 4.0.5 has been released, the problem does not occur when using that version. Praise be!

Related

Uploading latest version of Excel data like 2013 into SQL Server table without installing Access database engine

I get this error
The 'Microsoft.ACE.OLEDB.12.0' provider is not registered on the local machine
I get this error when I try to upload Excel 2013 data into a SQL Server table using .NET. I need to solve this error without installing the Access database engine.
Here is one option - https://www.simple-talk.com/sql/ssis/moving-data-from-excel-to-sql-server-10-steps-to-follow/
Another option would be to use OleDbConnection to read data from the excel file and then insert that data to the sql server -Accessing Excel 2013 File in ASP.NET
Thank you for your response Mr.ArunGeorge. I solved my question .
I would use EPPlus and SqlBulkCopy for this. It saves having to install anything on the server, like Office or the Access database connector (ACE), and SqlBulkCopy is a very fast way to insert data into SQL Server from C# (or VB) code. Also, EPPlus will create an Excel workbook in memory from a stream which means that you don't have to save the uploaded Excel file to the server, thereby saving you from having to mess about with file permissions or clearing out saved files that are no longer needed.
You can get EPPlus from Nuget (install-package EPPlus). Once you have it, here's a very simple Web Forms example showing how to use it. First, the aspx file which only has a FileUpload and a Button:
<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
<asp:FileUpload ID="Upload" runat="server" />
<asp:Button ID="Button1" runat="server" Text="Upload" />
</asp:Content>
Then the code-behind:
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack && Upload.HasFile)
{
if(Path.GetExtension(Upload.FileName).Equals(".xlsx"))
{
var excel = new ExcelPackage(Upload.FileContent);
var dt = excel.ToDataTable();
using (var conn = new SqlConnection("Server=.;Database=YourDatabase;Integrated Security=SSPI"))
{
var bulkCopy = new SqlBulkCopy(conn);
bulkCopy.DestinationTableName = "YourTable";
foreach (DataColumn column in dt.Columns)
{
//assumes that the Excel column names match the import table exactly
bulkCopy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
}
conn.Open();
bulkCopy.WriteToServer(dt);
}
}
}
}
You will need the following additional using statements at the top of the file:
using OfficeOpenXml;
using System.Data;
using System.Data.SqlClient;
And you will need the following extension method. Just create a class file called ExcelPackageExtensions and paste the following code into it:
public static class ExcelPackageExtensions
{
public static DataTable ToDataTable(this ExcelPackage package)
{
ExcelWorksheet workSheet = package.Workbook.Worksheets.First();
DataTable table = new DataTable();
foreach (var firstRowCell in workSheet.Cells[1, 1, 1, workSheet.Dimension.End.Column])
{
table.Columns.Add(firstRowCell.Text);
}
for (var rowNumber = 2; rowNumber <= workSheet.Dimension.End.Row; rowNumber++)
{
var row = workSheet.Cells[rowNumber, 1, rowNumber, workSheet.Dimension.End.Column];
var newRow = table.NewRow();
foreach (var cell in row)
{
newRow[cell.Start.Column - 1] = cell.Text;
}
table.Rows.Add(newRow);
}
return table;
}
}
You obviously need to change the connection string and table name to suit your needs, and if you have a mismatch between column names in the Excel file and the database (or don't have a header row in the Excel sheet) you will have to map columns individually instead of generically like I have here.

REST API (JSON) that updates SQL Table using Windows Console Application and Scheduled Tasks

I am a newbie at JSON programming. Most of my experience is in C# and some in XML and Javascript. So I am a bit lost. I will attempt to be as specific as possible.
I have written a windows console application that runs via the task scheduler. Basically the windows application is supposed to take the API from a site that is managed by an outside company but the information is owned by my company and put the information within a SQL table. The API is pretty standard and written in JSON.
I am successful in parsing the JSON language and (for example) displaying it in a command prompt but I need to be able to parse the language and place it into an SQL table. I have read up on SQL injection attacks and I feel fairly confident that we have covered our bases here. So the problem lies in the fact that it does not update the table when the application is run via the scheduler or without the scheduler.
I have included a little bit of the JSON language below along with the language for my console application.
{"date":"2015-09-24","data":[{"cid":"17","rank":1},{"cid":"26","rank":1},{"cid":"80","rank":1},{"cid":"30","rank":1},{"cid":"90","rank":1},{"cid":"62","rank":1},{"cid":"147","rank":1},{"cid":"28","rank":1}"s":1,"e":null}
using System;
using System.Collections;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Formatting;
using System.Threading.Tasks;
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace JsonApiClient
{
class Program
{
static void Main(string[] args)
{
ExecuteRiskSearch();
Console.ReadLine();
}
static void ExecuteRiskSearch()
{
string url = "https://localhost/api/getWatchList/";
string json = new WebClient().DownloadString(url);
JObject results = JObject.Parse(json);
foreach (var result in results)
{
string cid = (string)results["CID"];
JToken rank = results["rank"];
string risk = "";
if (rank is JValue)
{
risk = (string)rank;
}
else if (rank is JArray)
{
risk = (string)((JArray)rank).First;
}
else
{
SqlConnection connection = null;
SqlCommand command = null;
try
{
connection = new SqlConnection("Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=apiData;Data Source=serverName;");
command = new SqlCommand("UPDATE apiData.dbo.API SET [Category] WHERE CID=CID", connection);
connection.Open();
int numrows = command.ExecuteNonQuery();
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.Message);
}
finally
{
command.Dispose();
connection.Dispose();
}
}
}
}
}
}
What am I missing to make the JSON data update my SQL table? I have scoured Google search results and I haven't found much information. Any help would be so greatly appreciated.
For the need to foreach with the correct part of the JSON object, what I mean is very simply that your variable results includes the entire JSON object, from the "date" through the "e". You need to start with the "data" object and iterate through its array or your string cid will error out on assignment, as it will be attempting to assign an array to a single value. The same goes for your JToken rank. I believe it should be this:
foreach(datum in results["data"])
{
string cid = datum["cid"];
JToken rank = datum["rank"];
/* ... */
}
In addition, your set command isn't doing anything. You need to use SET columName = " + newValue + " WHERE CID == " + cid to actually affect a change, where columnName is the column you wish to alter and newValue is your C# variable carrying the desired replacement.
It's also a best practice to include a change to an updated date field when updating via an automated process, if there is one present. Generally the convention is to have a created date and an updated date for each row in a table.
I hope this at least points you in the right direction.
-C§
As an alternative, you can send entire text to Sql Server and load it there.
Sql Server 2016 will enable you to store JSON using single command - OPENJSON. In the older versions you can use existing CLR/JSON libraries such as Json4Sql or JsonSelect.

Error when uploading file to SP - could not find file: c:\windows\system32\inetsrv\

I'm uploading a file to a SharePoint Document library using the following code:
string fileToUpload = oFile.PostedFile.FileName;
using (SPSite oSite = new SPSite(spsite))
{
using (SPWeb oWeb = oSite.OpenWeb())
{
SPList library = oWeb.Lists[documentLibraryName];
using (FileStream fs = new FileStream(fileToUpload, FileMode.Open))
{
//more logic here
This works fine in my dev environment, but when I move it to QA, I get the following error when try to upload a file:
System.IO.FileNotFoundException:
Could not find file 'c:\windows\system32\inetsrv\file_to_upload'.
I have googled around, and it seems like I may need to use PostedFile.InputStream instead of PostedFile.Name.
But if set fileToUpload equal to oFile.PostedFile.InputStream, then I am not able to use this bit of code anymore:
using (FileStream fs = new FileStream(fileToUpload, FileMode.Open))
I would like to still use this code as I need to access fs.name later on in my code.
Any idea how I could get this problem resolved?
Give it a try, this might be helpful
https://social.msdn.microsoft.com/Forums/sharepoint/en-US/96f81cea-12b2-46f0-83ec-64c7fd9532cd/using-aspnet-fileupload-control-in-sharepoint-2010-announcement-list-newformaspx?forum=sharepointdevelopmentprevious

Using SMO Library in SSIS 2008

Good afternoon,
Im having some trouble writing a simple script using SMO objects in C# to validate if an object exists and then create it. This code is within a Script Task Component in SSIS. The code executes successfully, however the new database does not show up on my local instance. Any help would be greatly appreciated.
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.Collections;
using System.Data.SqlClient;
using Microsoft.SqlServer.Management.Common;
using Microsoft.SqlServer.Management.Smo;
public void Main()
{
//String DBName = Dts.Variables["TmpViewDBName"].Value.ToString();
//String Instance = Dts.Variables["TmpViewDBInstance"].Value.ToString();
String DBName = "localhost";
String Instance = "TmpViewDB";
Server TmpViewServer = new Server(Instance);
//Windows Auth
TmpViewServer.ConnectionContext.LoginSecure = true;
TmpViewServer.ConnectionContext.Connect();
if (TmpViewServer.Databases[DBName] != null)
{
TmpViewServer.Databases[DBName].Drop();
}
Database TmpViewDB = new Database(TmpViewServer, DBName);
if (TmpViewServer.ConnectionContext.IsOpen)
TmpViewServer.ConnectionContext.Disconnect();
Dts.TaskResult = (int)ScriptResults.Success;
}
I believe you need to add a line to actually create the object. As it stands now, you've only instantiated the object but never actually made the call to the database to create the remote object.
Database TmpViewDB = new Database(TmpViewServer, DBName);
TmpViewDB.Create();

export to excel from vb.net from sql datareader

I have an sql query with sql datareader. i put a for loop for the data reader. now when the data starts coming in from the query i want it to export to excel in the for loop.
here's my code
Try
Dim SqlStr As String = "", dr As SqlDataReader = Nothing
ConnectDB(Cnn)
Str = "query"
SqlCmd = New SqlCommand(Str, Cnn)
dr = SqlCmd.ExecuteReader
while dr.read
..EXPORT TO EXCEL
do
end while
does anyone know how to do this?
There are a number of approaches you can use
Decide if you are going to create a file on the server that can be referenced in a hyperlink
Stream the response to the client in real time, setting the mime type so that the response is loaded in excel.
For either approach, you need to create an output that Excel will open. There are some options here:
You can use spreadsheet xml. This is an xml format older versions of Excel can open.
You can write html into a file and rename it with a .xls extension/ write html to the response stream and set the mime type to excel.
Use OOXML to create an xlsx file. This file can be opened by all versions of Excel from Office 2000 onwards (client may need to download a free update to enable this). OOXML is quite complex so I suggest using a free library such as Simple OOXML. You can also download the OOXML SDK from that link.
See my blog post here for information on writing an xlsl document to the response stream.
This sample will render the entire Grid content to an Excel file.
private void _Click(object sender, System.EventArgs e)
{
//export to excel
Response.Clear();
Response.Buffer= true;
Response.ContentType = "application/vnd.ms-excel";
Response.Charset = "";
this.EnableViewState = false;
System.IO.StringWriter oStringWriter = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter oHtmlTextWriter =
new System.Web.UI.HtmlTextWriter(oStringWriter);
this.ClearControls(dg);
dg.RenderControl(oHtmlTextWriter);
Response.Write(oStringWriter.ToString());
Response.End();
}
I read about saving dataset to excel on CodeProject. You can refer to that article: http://www.codeproject.com/KB/dotnet/ExportToExcel.aspx
or http://www.codeproject.com/KB/aspnet/Export_large_data_to_xl.aspx