Uploading an Excel file to SQL through an ASP.NET webform

The method below describes how to upload a file to a webserver and then import the file into SQL using either LinqToSQL or SQL Bulk Copy.

Edit on Feb 18, 2010 : To be clear, this method shows how to extract the contents of an excel file into a sql server table as columns. This is not about uploading an excel file and storing it in SQL binary column.

The sample code only shows how to import xls and xlsx files but it could easily be extended to support csv files too.  Sample code is attached at the bottom.

fileupload

We will be uploading data from an Excel file containing columns CompanyName and Phone and loading that into the Northwind Shippers table.

We’ll start by uploading the file to the webserver. This is done with the help of the FileUpload web control. The FileUpload control has a SaveAs method which saves the contents of the file into the location that we specify. The file will be stored in a temp folder under App_Data since App_Data is not browsable directly by users.

Once we have successfully uploaded the file to the webserver, we use an OleDbConnection and an OleDbDataReader to read each row from the Excel file. The OleDb connection string varies by file extension. The connection strings are shown below:

Extension ConnectionString
xls Provider=Microsoft.Jet.OLEDB.4.0;Data Source=[FilePath];Extended Properties=”Excel 8.0;HDR=YES;IMEX=1”
xlsx Provider=Microsoft.ACE.OLEDB.12.0;Data Source=[FilePath];Extended Properties=Excel 12.0 Xml;HDR=YES;IMEX=1

HDR=Yes specifies that the first row of the data contains column names and not data. If you don’t want the first row to be skipped, change this to No.
IMEX=1
  specifies that the driver should always read the “intermixed” data columns as text.

The query we will be using with the connection is "SELECT CompanyName, Phone FROM [Sheet1$]". This assumes that we have an excel sheet called Sheet1 with header columns CompanyName and Phone.

Method 1: Using LINQ To SQL

Using the OleDBDataReader, we read each record and create a new Shipper object for each OleDbDataReader record as shown below. We add this object to the Shipper collection object that is associated with the Shipper table in the database using InsertOnSubmit and call SubmitChanges. This loads all the Excel records into the Shipper table.

Note: Since we are calling SubmitChanges without any Transaction defined, LINQ to SQL automatically starts a local transaction and uses it to execute the insert statements. When all insert statements successfully complete, LINQ to SQL commits the local transaction – nice:-) This occurs behind the scenes.

//ref: http://msdn.microsoft.com/en-us/library/system.data.oledb.oledbdatareader(VS.71).aspx
using (var context = new NorthwindDataContext())
{
using (var myConnection = new OleDbConnection(base.SourceConnectionString))
using (var myCommand = new OleDbCommand(query, myConnection))
{
myConnection.Open();
var myReader = myCommand.ExecuteReader();
while (myReader.Read())
{
context.Shippers.InsertOnSubmit(new Shipper()
{
CompanyName = myReader.GetString(0),
Phone = myReader.GetString(1)
});
}
}

context.SubmitChanges();
}

Method 2: Using SQL BulkCopy

With the BulkCopy method, we first have to define the Column Mappings since we will not be inserting data into the autogenerated ShipperID Primary Key column. The first column in the Excel file (CompanyName) has to be mapped to the second column in the Shipper table and the second column (Phone) has to be mapped to the third column in the Shipper table as shown below.

We read each record from the OleDbDataReader and using the BulkCopy WriteToServer overload that takes in an IDataReader (which the OleDbDataReader implements). The BulkCopy, using this method bulk loads the Shippers destination table with the data from the OleDbDatareader.

//ref: http://msdn.microsoft.com/en-us/library/system.data.oledb.oledbdatareader(VS.71).aspx
//ref: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx
using (var myConnection = new OleDbConnection(base.SourceConnectionString))
using (var destinationConnection = new SqlConnection(destinationConnectionString))
using (var bulkCopy = new SqlBulkCopy(destinationConnection))
{
    //Map first column in source to second column in sql table (skipping the ID column).
    //Excel schema[CompanyName,Phone] Table schema[ShipperID, CompanyName, Phone]
    bulkCopy.ColumnMappings.Add(0, 1);
 
    bulkCopy.ColumnMappings.Add(1, 2);
    bulkCopy.DestinationTableName = "dbo.Shippers";
 
    using (var myCommand = new OleDbCommand(query, myConnection))
    {
        myConnection.Open();
        destinationConnection.Open();
 
        var myReader = myCommand.ExecuteReader();
        bulkCopy.WriteToServer(myReader);
 
    }
}
 

You will need the 2007 Office System Driver : Data Connectivity Components installed on your server also. 

The BulkCopy object is much faster than LINQ to SQL. I am copying Pablo Castro’s newsgroup response:

  • No per-row statement execution. When you do multiple inserts without bulk-copy, each insert is a statement in itself (regardless of whether it's batched together with other statements). With bulk-copy, we don't incur the cost of executing a statement for each row, the whole copy operation is a single thing.
  • No multiple network round-trips. Once the bulk-insert operation is setup, we send rows from the client to the server continously, without going back-and-forth over the wire.
  • Server storage engine also can greatly optimize how rows are inserted when performing a bulk-copy operation. How much can be optimized depends a lot on the recovery model the tarder database is set to; in "simple" and "bulk logged" the overhead of logging is greatly reduced during bulk-copy operations, helping a lot with performance.

Sample Code
Mirror

15 Comments

  • Nice Article, I learned from you article that there can be many consecutive usings for single code block

  • Microsoft Excel has to be installed in the server to run this code?

  • >Microsoft Excel has to be installed in the server to run this code?

    No, but you need 2007 Office System Driver : Data Connectivity Components. I have updated the post with a link to the download.

    Hope that helps,
    Raj

  • thanks for the informative article but during the insert the first row after the header is being skipped. Whats the sloution for this

  • ravio,

    HDR=Yes specifies that the first row of the data contains column names and not data

    If you don't want the first row skipped, set it to No.

  • pls send me a copy of this and what if my connection to database will be on sql and not acess??

  • It works fine in local system, But when it deploye in server I am getting an error -404 file or directory not found. plz send whats the isseue?

  • When using the bulkcopy approach the first record is being skipped because of the myreader.read

    Code in sample:
    while (myReader.Read())
    {
    bulkCopy.WriteToServer(myReader);
    }

    change to
    bulkCopy.WriteToServer(myReader);

    once this is done, the first record in your source file we be inserted into the destination.

  • Thanks Jag! Fixed.

  • I exactly use the same code for reading the XlsX file and installed the office 2007 System driver,
    It keeps giving me error
    "Could not find installable ISAM."
    when I try to open oledbconnection
    myConnection.Open();

    Please help

  • What if I need to insert few columns with a fixed value and the remaining with the data from the excel sheet? For Ex. Class Code to be inserted from the code and the remaining data like Student Name, Roll No, Address, Place, Zipcode, Phone is being inserted from the Excel sheet. Class code can't be placed in the Excel Sheet as it will be a unique code.

  • I quote "The 2007 Office System Driver are not intended:


    1.As a general replacement for Jet (If you need a general replacement for Jet you should use SQL Server Express Edition).
    2.As a replacement for the Jet OLEDB Provider in server-side applications.
    3.As a general word processing, spreadsheet or database management system-To be used as a way to create files. (You can use Microsoft Office or Office automation to create the files that Microsoft Office supports.)
    4.To be used within a service program or web application that relies on a Windows service."

    and "
    This package contains components that are at the same version as the “The 2007 Microsoft Office Suite Service Pack 1 (SP1)” release.

    The Office System Drivers are only supported under certain scenarios, including:


    1.Desktop applications which read from and write to various files formats including Microsoft Office Access, Microsoft Office Excel and text files.
    2.To transfer data between supported file formats and a database repository such as SQL Server; in the context of a desktop application.
    "

    not to be used aas part of web application it seems!

  • Thanks for the information but i would like to know what to do incase i have 4 sheets in 1 excel workbook and all the four sheets are to be imported to 4 different tables.

  • hi , can upload the file sucesfully to the database , if i mention the path with " //" address i can upload excel easily . i need a code for upload control . so that the application can take excel file anywhere from clients pc and upload it to server without any errors . kindly help ..

  • I am trying to upload .xlsx file to sql server table.I have installed office 2007 componets in the server.I can upload upto 10000 rows.The file have 68000 rows.I increased IIS timout.Increased max pool size in connection string.But i am getting error in the server.The error is as follows."System.Data.SqlClient.SqlException: Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding."

Comments have been disabled for this content.