You can use a library called ExcelLibrary. It's a free, open source library posted on Google Code:
ExcelLibrary
This looks to be a port of the PHP ExcelWriter that you mentioned above. It will not write to the new .xlsx format yet, but they are working on adding that functionality in.
It's very simple, small and easy to use. Plus it has a DataSetHelper that lets you use DataSets and DataTables to easily work with Excel data.
ExcelLibrary seems to still only work for the older Excel format (.xls files), but may be adding support in the future for newer 2007/2010 formats.
You can also use EPPlus, which works only for Excel 2007/2010 format files (.xlsx files). There's also NPOI which works with both.
There are a few known bugs with each library as noted in the comments. In all, EPPlus seems to be the best choice as time goes on. It seems to be more actively updated and documented as well.
Also, as noted by @АртёмЦарионов below, EPPlus has support for Pivot Tables and ExcelLibrary may have some support (Pivot table issue in ExcelLibrary)
Here are a couple links for quick reference:
ExcelLibrary - GNU Lesser GPL
EPPlus - GNU (LGPL) - No longer maintained
EPPlus 5 - Polyform Noncommercial - Starting May 2020
NPOI - Apache License
Here some example code for ExcelLibrary:
Here is an example taking data from a database and creating a workbook from it. Note that the ExcelLibrary code is the single line at the bottom:
//Create the data set and table
DataSet ds = new DataSet("New_DataSet");
DataTable dt = new DataTable("New_DataTable");
//Set the locale for each
ds.Locale = System.Threading.Thread.CurrentThread.CurrentCulture;
dt.Locale = System.Threading.Thread.CurrentThread.CurrentCulture;
//Open a DB connection (in this example with OleDB)
OleDbConnection con = new OleDbConnection(dbConnectionString);
con.Open();
//Create a query and fill the data table with the data from the DB
string sql = "SELECT Whatever FROM MyDBTable;";
OleDbCommand cmd = new OleDbCommand(sql, con);
OleDbDataAdapter adptr = new OleDbDataAdapter();
adptr.SelectCommand = cmd;
adptr.Fill(dt);
con.Close();
//Add the table to the data set
ds.Tables.Add(dt);
//Here's the easy part. Create the Excel worksheet from the data set
ExcelLibrary.DataSetHelper.CreateWorkbook("MyExcelFile.xls", ds);
Creating the Excel file is as easy as that. You can also manually create Excel files, but the above functionality is what really impressed me.
The source referenced by the OP has some credibility ...but what about Microsoft - what is the stance on struct usage? I sought some extra learning from Microsoft, and here is what I found:
Consider defining a structure instead of a class if instances of the
type are small and commonly short-lived or are commonly embedded in
other objects.
Do not define a structure unless the type has all of the following characteristics:
- It logically represents a single value, similar to primitive types (integer, double, and so on).
- It has an instance size smaller than 16 bytes.
- It is immutable.
- It will not have to be boxed frequently.
Microsoft consistently violates those rules
Okay, #2 and #3 anyway. Our beloved dictionary has 2 internal structs:
[StructLayout(LayoutKind.Sequential)] // default for structs
private struct Entry //<Tkey, TValue>
{
// View code at *Reference Source
}
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct Enumerator :
IEnumerator<KeyValuePair<TKey, TValue>>, IDisposable,
IDictionaryEnumerator, IEnumerator
{
// View code at *Reference Source
}
*Reference Source
The 'JonnyCantCode.com' source got 3 out of 4 - quite forgivable since #4 probably wouldn't be an issue. If you find yourself boxing a struct, rethink your architecture.
Let's look at why Microsoft would use these structs:
- Each struct,
Entry
and Enumerator
, represent single values.
- Speed
Entry
is never passed as a parameter outside of the Dictionary class. Further investigation shows that in order to satisfy implementation of IEnumerable, Dictionary uses the Enumerator
struct which it copies every time an enumerator is requested ...makes sense.
- Internal to the Dictionary class.
Enumerator
is public because Dictionary is enumerable and must have equal accessibility to the IEnumerator interface implementation - e.g. IEnumerator getter.
Update - In addition, realize that when a struct implements an interface - as Enumerator does - and is cast to that implemented type, the struct becomes a reference type and is moved to the heap. Internal to the Dictionary class, Enumerator is still a value type. However, as soon as a method calls GetEnumerator()
, a reference-type IEnumerator
is returned.
What we don't see here is any attempt or proof of requirement to keep structs immutable or maintaining an instance size of only 16 bytes or less:
- Nothing in the structs above is declared
readonly
- not immutable
- Size of these struct could be well over 16 bytes
Entry
has an undetermined lifetime (from Add()
, to Remove()
, Clear()
, or garbage collection);
And ...
4. Both structs store TKey and TValue, which we all know are quite capable of being reference types (added bonus info)
Hashed keys notwithstanding, dictionaries are fast in part because instancing a struct is quicker than a reference type. Here, I have a Dictionary<int, int>
that stores 300,000 random integers with sequentially incremented keys.
Capacity: 312874
MemSize: 2660827 bytes
Completed Resize: 5ms
Total time to fill: 889ms
Capacity: number of elements available before the internal array must be resized.
MemSize: determined by serializing the dictionary into a MemoryStream and getting a byte length (accurate enough for our purposes).
Completed Resize: the time it takes to resize the internal array from 150862 elements to 312874 elements. When you figure that each element is sequentially copied via Array.CopyTo()
, that ain't too shabby.
Total time to fill: admittedly skewed due to logging and an OnResize
event I added to the source; however, still impressive to fill 300k integers while resizing 15 times during the operation. Just out of curiosity, what would the total time to fill be if I already knew the capacity? 13ms
So, now, what if Entry
were a class? Would these times or metrics really differ that much?
Capacity: 312874
MemSize: 2660827 bytes
Completed Resize: 26ms
Total time to fill: 964ms
Obviously, the big difference is in resizing. Any difference if Dictionary is initialized with the Capacity? Not enough to be concerned with ... 12ms.
What happens is, because Entry
is a struct, it does not require initialization like a reference type. This is both the beauty and the bane of the value type. In order to use Entry
as a reference type, I had to insert the following code:
/*
* Added to satisfy initialization of entry elements --
* this is where the extra time is spent resizing the Entry array
* **/
for (int i = 0 ; i < prime ; i++)
{
destinationArray[i] = new Entry( );
}
/* *********************************************** */
The reason I had to initialize each array element of Entry
as a reference type can be found at MSDN: Structure Design. In short:
Do not provide a default constructor for a structure.
If a structure defines a default constructor, when arrays of the
structure are created, the common language runtime automatically
executes the default constructor on each array element.
Some compilers, such as the C# compiler, do not allow structures to
have default constructors.
It is actually quite simple and we will borrow from Asimov's Three Laws of Robotics:
- The struct must be safe to use
- The struct must perform its function efficiently, unless this would violate rule #1
- The struct must remain intact during its use unless its destruction is required to satisfy rule #1
...what do we take away from this: in short, be responsible with the use of value types. They are quick and efficient, but have the ability to cause many unexpected behaviors if not properly maintained (i.e. unintentional copies).
Best Answer
I hate answering my own questions:
If you are not administrator VS can not manage "Local IIS Web Server" properly.
Also if I'm not wrong, "IIS Express" can not load x64 Sync Framework assemblies even if your OS is x64.