Files
sqltoolsservice/src/Microsoft.SqlTools.ServiceLayer/QueryExecution/DataStorage/SaveAsWriterBase.cs
Wujun Zhou 8d47d5c7b3 Save As Excel (#279)
* Fix dispose pattern usage in SaveAsWriterBase

* Add SaveAsExcel feature

This adds the save as excel function to the backend. To reduce large dependency and run on dotnet core now, this implementation use a raw excel writer (the SaveAsExcelFileStreamWriterHelper.cs) instrad of popular excel library, such as EPPlus or OpenXmlSdk.

* Fix can not open the generated excel file in google sheet

For the file name inside excel, google uses a case sensitive path while
Excel doesn't. This change fix the case, so that the file name matches
the one in x1/_rels/workbook.xml.rels

* Fix datetime doesn't recognized by google sheet

Google doesn't support cell type t="d" with ISO 8601 date. (From stackoverflow thread and testing), thus use the old way of excel datetime, which uses double to present datetime

* update to use xmlwriter

* Add basic unit tests for SaveAsExcelFileStreamWriterHelper

* refactor: simplify the public interface of the SaveAsExcelFileStreamWriterHelper

* update private fields names based on the name convention

* Add comments to classes of SaveAsExcel feature

* clean up SaveAsExcelFileStreamWriterHelper

- change SaveAsExcelFileStreamWriterHelper from public to internal
- remove the PenddingRowEndTag function from referenceManager
- change the SaveAsExcelFileStreamWriterHelper(stream) to default leaveOpen to false to match the normal behavior
- change the rowreference to use XmlConvert to convert int to string
- rename writeSetting to writerSetting and add private

* fix CI test error for SaveAsExcel

* remove ExporterException in SaveAsExcel

* fix lefe over CSV to Excel in the comments

* refactor to be consistent with JsonWriter and remove the comment

* remove commented out test

The test is too slow to run

* fix typo in comment

* refactor SaveAsExcel to the coding standard

* refactor rewrite the WriteStyle with XmlWriter

* Add licence header

* reverse mistakenly checked-in changes

* fix: left-over CSV in commets

* remove duplicate check

The check was done in the IncreaseColumnReference, but that check is too late in case of too many columns. All the addCell do the check at the begining now

* fix TimeSpan more than 24 hours

* fix AddRowMustBeCalledBeforeAddCellException test

This is  due to remove duplicate call to AssureColumnReference in  WriteAndIncreaseColumnReference

* fix: TimeSpan will write twice

* style: change retun in the switch to break

* Add bool format

* remove todo in comment

This provides extra safeguard in the cost of one memory access when null.
2017-03-16 14:33:12 -07:00

124 lines
4.0 KiB
C#

//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.SqlTools.ServiceLayer.QueryExecution.Contracts;
namespace Microsoft.SqlTools.ServiceLayer.QueryExecution.DataStorage
{
/// <summary>
/// Abstract class for implementing writers that save results to file. Stores some basic info
/// that all save as writer would need.
/// </summary>
public abstract class SaveAsStreamWriter : IFileStreamWriter
{
/// <summary>
/// Stores the internal state for the writer that will be necessary for any writer.
/// </summary>
/// <param name="stream">The stream that will be written to</param>
/// <param name="requestParams">The SaveAs request parameters</param>
protected SaveAsStreamWriter(Stream stream, SaveResultsRequestParams requestParams)
{
FileStream = stream;
var saveParams = requestParams;
if (requestParams.IsSaveSelection)
{
// ReSharper disable PossibleInvalidOperationException IsSaveSelection verifies these values exist
ColumnStartIndex = saveParams.ColumnStartIndex.Value;
ColumnEndIndex = saveParams.ColumnEndIndex.Value;
ColumnCount = saveParams.ColumnEndIndex.Value - saveParams.ColumnStartIndex.Value + 1;
// ReSharper restore PossibleInvalidOperationException
}
}
#region Properties
/// <summary>
/// Index of the first column to write to the output file
/// </summary>
protected int? ColumnStartIndex { get; private set; }
/// <summary>
/// Number of columns to write to the output file
/// </summary>
protected int? ColumnCount { get; private set; }
/// <summary>
/// Index of the last column to write to the output file
/// </summary>
protected int? ColumnEndIndex { get; private set; }
/// <summary>
/// The file stream to use to write the output file
/// </summary>
protected Stream FileStream { get; private set; }
#endregion
/// <summary>
/// Not implemented, do not use.
/// </summary>
[Obsolete]
public int WriteRow(StorageDataReader dataReader)
{
throw new InvalidOperationException("This type of writer is meant to write values from a list of cell values only.");
}
/// <summary>
/// Writes a row of data to the output file using the format provided by the implementing class.
/// </summary>
/// <param name="row">The row of data to output</param>
/// <param name="columns">The list of columns to output</param>
public abstract void WriteRow(IList<DbCellValue> row, IList<DbColumnWrapper> columns);
/// <summary>
/// Not implemented, do not use.
/// </summary>
[Obsolete]
public void Seek(long offset)
{
throw new InvalidOperationException("SaveAs writers are meant to be written once contiguously.");
}
/// <summary>
/// Flushes the file stream buffer
/// </summary>
public void FlushBuffer()
{
FileStream.Flush();
}
#region IDisposable Implementation
private bool disposed;
/// <summary>
/// Disposes the instance by flushing and closing the file stream
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
FileStream.Dispose();
}
disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}
}