OPserver: SQL CPU Graph Not Displayed - stack-overflow

i am using opserver tool to monitor SQL performance everything works fine except it is not display CPU graph sparks for both standalone and cluster

i replace SQLCPUSpark function with this code
public ActionResult SQLCPUSpark(string node)
{
var instance = SQLInstance.Get(node);
if (instance == null)
return ContentNotFound("SQLNode not found with name = '" + node + "'");
var dataPoints = instance.CPUHistoryLastHour;
var chart = new Chart();
var area = new ChartArea();
area.AxisX.Enabled = AxisEnabled.False;
area.AxisY.Enabled = AxisEnabled.False;
area.AxisY.Maximum = 100;
// configure your chart area (dimensions, etc) here.
chart.ChartAreas.Add(area);
// create and customize your data series.
var series = new Series();
foreach (var item in dataPoints.Data)
{
series.Points.AddXY(item.EventTime.ToOADate(), item.ProcessUtilization);
}
series.Label = "";
series.Font = new Font("Segoe UI", 8.0f, FontStyle.Bold);
series.ChartType = SeriesChartType.Area;
chart.Series.Add(series);
return chart.ToResult();
}

Related

Dynamic report columns in BIRT

I have a BIRT report, which simply selects all the columns of a table. Every time a new column is added to the table I have to modify the report to visualize the new column. Is it possible somehow to show the result of a "select * from table" query in the report, so I shouldn't modify the report template anymore? The order of columns is not important.
Thank You.
This is not directly possible.
However, using the DE API, you can create and save a new rptdesign file based on the query, then in a second step execute that report.
The example at https://www.eclipse.org/birt/documentation/integrating/deapi.php only shows how to create layout items, but you can as well create Data Sources and Data Sets.
This is a bit tricky however, so unless you need this quite often, it is not worth the effort.
Here is an excerpt from a script I am using in a file sql2rptdesign.rptdesign to create a new report design. This should give you an idea:
importPackage(Packages.org.eclipse.birt.data.engine.api);
importPackage(Packages.org.eclipse.birt.data.engine.core);
importPackage(Packages.org.eclipse.birt.report.model.api);
importPackage(Packages.org.eclipse.birt.report.model.api.elements.structures);
importPackage(Packages.org.eclipse.birt.report.model.api.util);
importPackage(Packages.org.eclipse.birt.data.engine.api.querydefn);
var engine = reportContext.getReportRunnable().getReportEngine();
var myconfig = reportContext.getReportRunnable().getReportEngine().getConfig();
var designFactory = designHandle.getElementFactory();
// Ein neues DataSet anlegen
var de = DataEngine.newDataEngine( myconfig, null );
var dsrc = reportContext.getDesignHandle().findDataSource("lisa");
var dsHandle = designFactory.newOdaDataSet("Main", "org.eclipse.birt.report.data.oda.jdbc.JdbcSelectDataSet" );
dsHandle.setDataSource(dsrc.getName());
dsHandle.setQueryText(sql);
designHandle.getDataSets( ).add( dsHandle );
Then I add the output columns (I parsed the SQL statement beforehand to get the necessary information). I don't know if this is strictly needed.
var numColumns = 0;
for (var position=0; position<rows.length; position++) {
var row = rows[position];
var colName = row[0];
if (colName == "__Template") {
continue;
}
var colTyp = row[1];
var colWidth = row[2];
var resultCol = new OdaResultSetColumn();
resultCol.setColumnName(colName);
resultCol.setNativeName(colName);
resultCol.setDataType(colTyp);
resultCol.setPosition(position+1);
numColumns = position + 1;
dsHandle.getPropertyHandle(DataSetHandle.RESULT_SET_PROP).addItem(resultCol);
var resultHint = new ColumnHint();
resultHint.setProperty(ColumnHint.COLUMN_NAME_MEMBER, colName);
dsHandle.getPropertyHandle(DataSetHandle.COLUMN_HINTS_PROP).addItem(resultHint);
}
And then add a table, its column bindings, columns, detail rows and cell contents to the layout. This is a bit lenghty.
// Tabellarisches Layout
var elementFactory = designHandle.getElementFactory();
var body = designHandle.getBody();
var tab = elementFactory.newTableItem("Main", numColumns);
tab.setDataSet(dsHandle);
tab.pageBreakInterval = 9999;
for (var colNum=0; colNum<numColumns; colNum++) {
var r = rows[colNum];
var colName = r[0];
var colNameSlashes = addSlashes(colName);
var colType = r[1];
var width = r[2];
var pattern = r[3];
var align = r[4];
// Binding
var colBinding = StructureFactory.createComputedColumn( );
colBinding.setName(colName);
colBinding.setExpression( 'dataSetRow[' + colNameSlashes + ']');
colBinding.setDataType(colType);
tab.addColumnBinding(colBinding, true);
// Überschrift
var textdata = elementFactory.newTextData(null);
textdata.setContentType("plain");
textdata.setValueExpr(colNameSlashes);
textdata.setStringProperty("whiteSpace", "normal");
tab.getLayoutModel().getCell(1,colNum+1).getContent().add(textdata);
// Wert
var textdata = elementFactory.newTextData(null);
textdata.setContentType("plain");
textdata.setHasExpression(true);
textdata.setStringProperty("whiteSpace", "normal");
var expr = 'row[' + colNameSlashes + ']';
textdata.setValueExpr(expr);
tab.getLayoutModel().getCell(2,colNum+1).getContent().add(textdata);
// Spaltenbreite und Ausrichtung
var columnHandle = tab.getColumns().get(colNum); // 0-basiert?
if (width) {
columnHandle.setProperty("width", StringUtil.parse(width));
}
}
body.add(tab, 0);
And finally save the new file:
designHandle.saveAs(designFilename);
However, this doesn't use a template as you would like to do.

Enhancing Performance in Student Info App

I've built a simple Student Information platform using Google Sheets. It allows the user to query, update and create new student info on a user interface. Please refer to this sheet to see how it works.
The functions to Refresh/Update/Save are inside the Actions button on a menu bar. Everything seems to work well however when the number of records increases, say above 100 records, all the functions slow down and it gets extremely slow with 200+ records.
Appreciate if anyone could help to take a look at the scripts as I suspect they need to be optimized.
Many thanks in advance!
function UpdateDataIntoMaster() { //This script is used in the SAVE button in UPDATE sheet)
/*Get data from UPDATE Sheet*/
var ss = SpreadsheetApp.openById("11Djp9UmXbtWv7VitZFfo0X4Ctet3O8Amh4xADNKOZgY");
var sheet = ss.getSheetByName('UPDATE');
var range = sheet.getRange("D30:AE30"); //All data transposed into this line. MUST be updated if more fields are added into the Data sheet
var values = range.getValues();
var rangeForKey = sheet.getRange("D30") //Student Name is used as the
key identifier
var keyValue = rangeForKey.getValue();
/*Pass in keyValue(identifier = Student Name)
and all data in the function below in order
to update master data sheet*/
updDbase(keyValue,values);
function updDbase(keyValue,values) {
var ss = SpreadsheetApp.openById("11Djp9UmXbtWv7VitZFfo0X4Ctet3O8Amh4xADNKOZgY")
var sheet = ss.getSheetByName('Data');
var data = sheet.getDataRange().getValues();
var noOfRow = values.length
var noOfCol = values[0].length
for (var i=0; i < data.length; i++) { // going through all the rows in Data sheet
var keyData = ss.getSheetByName("Data").getRange(i+1,1).getValue(); //Get the Student Name from Data sheet
if (keyData == keyValue) {
// for (var j=0; j < data[i].length; j++) { // this is going through all the cell of a row
var row = Number(i)+1;
var sh = SpreadsheetApp.getUi();
var response = sh.alert("Update Information","Are you sure you want to update the student information?", sh.ButtonSet.YES_NO);
if (response == sh.Button.YES)
{
var sheets = ss.getSheetByName("Data").getRange(row,1,noOfRow,noOfCol).setValues(values);
}//If response == YES
}
}
}
}
function CreateNew() {
/*Get data from Inquiry Sheet*/
var ss = SpreadsheetApp.openById("11Djp9UmXbtWv7VitZFfo0X4Ctet3O8Amh4xADNKOZgY");
var sheetNew = ss.getSheetByName('Create New');
var range = sheetNew.getRange("D30:AZE30"); //All data transposed into
this line
var values = range.getValues();
var rangeForKey = sheetNew.getRange("E30") //Using Student ID as key identifier
var keyValue = rangeForKey.getValue();
var noOfRow = values.length
var noOfCol = values[0].length
var sheetData = ss.getSheetByName('Data');
var lastRow = sheetData.getLastRow();
var data = sheetData.getDataRange().getValues();
for (var i=0; i < data.length; i++) { // going through all the rows in Data sheet
var keyData = sheetData.getRange(i+1,2).getValue(); //Get the Student ID from Data sheet
if (keyData == keyValue) {
AlertBox();//If Student ID is found, to prompt Student ID already
exist
return;
} //If
} //For
/*Confirming with user whether to proceed to create new entry*/
var sh = SpreadsheetApp.getUi();
var response = sh.alert("Create New Record","Are you sure you want to
create new student information?", sh.ButtonSet.YES_NO);
if (response == sh.Button.YES){
if (keyValue == ""){
var response = sh.alert("Create New Record","Unable to proceed
because Student ID is empty", sh.ButtonSet.OK);
return;}
else {
//var response = sh.alert("Create New Record","Unable to
proceed because Student ID is empty", sh.ButtonSet.OK);
var sheets =
sheetData.getRange(lastRow+1,1,1,noOfCol).setValues(values)
}
}//If
}
function EditStudentInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Inquiry");
//var ss = SpreadsheetApp.getActive();
var protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
}
function EditContent() {
var s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Update");
var rangeContentCol1 = s.getRange("E3:E23");
var CopyContentCol1 = s.getRange("E3:E23").getValues();
var rangeContentCol2 = s.getRange("I3:I23");
var CopyContentCol2 = s.getRange("I3:I23").getValues();
rangeContentCol1.clearContent();
rangeContentCol2.clearContent();
var PasteContentCol1 =
s.getRange("E3:E23").setValues(CopyContentCol1);
var PasteContentCol2 = s.getRange("I3:I23").setValues(CopyContentCol2);
}
A common performance mistake people with Apps Script is doing the .getRange().getValues() within their for loops. Performance-wise these get and set calls are quite expensive.
Lucky the fix for this is quite easy - get all the data at once first, then loops through it. You actually do this already, sort of. In your script you get the whole data range, but then only use a part of the data and instead do another getValues call. I've updated the two areas in your script that had getRange() calls in a for loop --> var keyData = data[i][0];
function UpdateDataIntoMaster() { //This script is used in the SAVE button in UPDATE sheet)
/*Get data from UPDATE Sheet*/
var ss = SpreadsheetApp.openById("11Djp9UmXbtWv7VitZFfo0X4Ctet3O8Amh4xADNKOZgY");
var sheet = ss.getSheetByName('UPDATE');
var range = sheet.getRange("D30:AE30"); //All data transposed into this line. MUST be updated if more fields are added into the Data sheet
var values = range.getValues();
var rangeForKey = sheet.getRange("D30") //Student Name is used as the
key identifier
var keyValue = rangeForKey.getValue();
/*Pass in keyValue(identifier = Student Name)
and all data in the function below in order
to update master data sheet*/
updDbase(keyValue,values);
function updDbase(keyValue,values) {
var ss = SpreadsheetApp.openById("11Djp9UmXbtWv7VitZFfo0X4Ctet3O8Amh4xADNKOZgY")
var sheet = ss.getSheetByName('Data');
var data = sheet.getDataRange().getValues();
var noOfRow = values.length
var noOfCol = values[0].length
for (var i=0; i < data.length; i++) { // going through all the rows in Data sheet
var keyData = data[i][0]; //Use the data that is already loaded.
if (keyData == keyValue) {
// for (var j=0; j < data[i].length; j++) { // this is going through all the cell of a row
var row = Number(i)+1;
var sh = SpreadsheetApp.getUi();
var response = sh.alert("Update Information","Are you sure you want to update the student information?", sh.ButtonSet.YES_NO);
if (response == sh.Button.YES)
{
var sheets = ss.getSheetByName("Data").getRange(row,1,noOfRow,noOfCol).setValues(values);
}//If response == YES
}
}
}
}
function CreateNew() {
/*Get data from Inquiry Sheet*/
var ss = SpreadsheetApp.openById("11Djp9UmXbtWv7VitZFfo0X4Ctet3O8Amh4xADNKOZgY");
var sheetNew = ss.getSheetByName('Create New');
var range = sheetNew.getRange("D30:AZE30"); //All data transposed into
this line
var values = range.getValues();
var rangeForKey = sheetNew.getRange("E30") //Using Student ID as key identifier
var keyValue = rangeForKey.getValue();
var noOfRow = values.length
var noOfCol = values[0].length
var sheetData = ss.getSheetByName('Data');
var lastRow = sheetData.getLastRow();
var data = sheetData.getDataRange().getValues();
for (var i=0; i < data.length; i++) { // going through all the rows in Data sheet
var keyData = data[i][0]; //Use the data that is already loaded.
if (keyData == keyValue) {
AlertBox();//If Student ID is found, to prompt Student ID already
exist
return;
} //If
} //For
/*Confirming with user whether to proceed to create new entry*/
var sh = SpreadsheetApp.getUi();
var response = sh.alert("Create New Record","Are you sure you want to
create new student information?", sh.ButtonSet.YES_NO);
if (response == sh.Button.YES){
if (keyValue == ""){
var response = sh.alert("Create New Record","Unable to proceed
because Student ID is empty", sh.ButtonSet.OK);
return;}
else {
//var response = sh.alert("Create New Record","Unable to
proceed because Student ID is empty", sh.ButtonSet.OK);
var sheets =
sheetData.getRange(lastRow+1,1,1,noOfCol).setValues(values)
}
}//If
}
function EditStudentInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Inquiry");
//var ss = SpreadsheetApp.getActive();
var protections = ss.getProtections(SpreadsheetApp.ProtectionType.RANGE);
for (var i = 0; i < protections.length; i++) {
var protection = protections[i];
if (protection.canEdit()) {
protection.remove();
}
}
}
function EditContent() {
var s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Update");
var rangeContentCol1 = s.getRange("E3:E23");
var CopyContentCol1 = s.getRange("E3:E23").getValues();
var rangeContentCol2 = s.getRange("I3:I23");
var CopyContentCol2 = s.getRange("I3:I23").getValues();
rangeContentCol1.clearContent();
rangeContentCol2.clearContent();
var PasteContentCol1 =
s.getRange("E3:E23").setValues(CopyContentCol1);
var PasteContentCol2 = s.getRange("I3:I23").setValues(CopyContentCol2);
}
Give this a test and let me know if it helps!

Updating Panel with new values in GAS UI

I have a huge spreadsheet matrix, from which I create a long list of check boxes. The users then select different abilities, press search. The code the cross-checks with the database spreadsheet, returning names of the persons who has those abilities.
I need to update the "rightPanel" with the results of my search. But i simple can't figure out how to - if at all posible - update a panel in my UI..
var dataSSkey = 'sheetID'; //datasheet ID
var dataSheet = SpreadsheetApp.openById(dataSSkey).getSheetByName('Ansatte');
var groupsArray = [[],[],[],[]];
var lastRow = dataSheet.getLastRow();
var lastColumn = dataSheet.getLastColumn();
var dataArray = dataSheet.getRange(1,1,lastRow,lastColumn).getValues();
var numberGroups
var app = UiApp.createApplication().setTitle('Find Consultant');
var panel = app.createVerticalPanel();
var leftPanel = app.createVerticalPanel().setWidth(450);
var rightPanel = app.createVerticalPanel().setWidth(450);
var grid = app.createGrid(1, 2).setId('myGrid')
var outputArray = []; //to store output from search
var positiveList = [[],[]]; //array to store name and folder-ID of consultants matching
var numberPositive = 0; //number of consultants matching
function doGet() {
buildGroupsArray()
addCheckBoxesToUI()
var scrollPanel = app.createScrollPanel().setHeight(460);
//Search button
var searchButton = app.createButton('Search');
var clickHandler = app.createServerClickHandler("respondToSearch");
searchButton.addClickHandler(clickHandler);
clickHandler.addCallbackElement(panel);
var spacerImage = app.createImage("http://www.bi..ge.jpg").setHeight(3);
scrollPanel.add(panel);
rightPanel.add(app.createLabel('resultat her'));
leftPanel.add(scrollPanel);
leftPanel.add(spacerImage);
leftPanel.add(searchButton);
grid.setWidget(0, 0, leftPanel)
grid.setWidget(0, 1, rightPanel);
app.add(grid);
return app;
}
function respondToSearch(e){
var numberLogged = 0;
//define firstEmpty
var firstEmpty = "A"+lastRow;
if(lastRow !== 1){
firstEmpty = "A"+(lastRow+1);
};
//find selected competencies --> store in array + count competencies
for(i = 1; i <= lastRow; i++){
if (e.parameter["Checkbox"+i] == "true") {
var value = e.parameter["CheckboxValue"+i];
outputArray[numberLogged] = value;
numberLogged++;
}
}
for(i = 2; i <= lastColumn; i++){
var numberCorrect = 0;
//Run through rows according to content of output from selection
for(j in outputArray){
//Check if consultant own selected competency
if(dataArray[outputArray[j]][i] == "x"){
numberCorrect++; //if consultant owns selected competency then count
}
}
//if consultant owns all competencies, then add name and folder-id to array
if(numberCorrect == numberLogged){
positiveList[0][numberPositive] = dataArray[1][i]; //Add consultant name
positiveList[1][numberPositive] = dataArray[2][i]; //Add consultant-folder ID
numberPositive++ //count the number of consultants that own all competencies
}
}
for(j in positiveList[0]){
var name = positiveList[0][j];
var id = positiveList[1][j];
Logger.log(name);
Logger.log(id)
var anchor = app.createAnchor(name,'https://ww......folderviewid='+id);
rightPanel.add(anchor)
}
return app;
}
I don't really understand the problem you have...
In your handler function you only have to use app=UiApp.getActiveApplication() and from there populate the panel exactly the same way you did it in the doGet() function, ending with a return app; that will actually update the current Ui.
There are dozens of examples all around... did I misunderstand something in your question ?
Edit : following your comment.
I suppose you defined your variables outside of the doGet function hoping they will become global and so available to all the functions in your script but this is not going to work. Global variables in Google Apps script can't be updated by functions.
I would strongly recommend that you create app and panels in the doGet function and give them an ID so that you can get them back and update their values (or content) from the handler functions.
Here is a re-written version of your code (didn't test)
: (some parts are not reproduced (see //...)
var dataSSkey = 'sheetID'; //datasheet ID
var dataSheet = SpreadsheetApp.openById(dataSSkey).getSheetByName('Ansatte');
var groupsArray = [[],[],[],[]];
var lastRow = dataSheet.getLastRow();
var lastColumn = dataSheet.getLastColumn();
var dataArray = dataSheet.getRange(1,1,lastRow,lastColumn).getValues();
var numberGroups
var outputArray = []; //to store output from search
var positiveList = [[],[]]; //array to store name and folder-ID of consultants matching
var numberPositive = 0; //number of consultants matching
function doGet() {
var app = UiApp.createApplication().setTitle('Find Consultant');
var panel = app.createVerticalPanel();
var leftPanel = app.createVerticalPanel().setWidth(450).setId('leftPanel');
var rightPanel = app.createVerticalPanel().setWidth(450).setId('rightPanel');;
var grid = app.createGrid(1, 2).setId('myGrid')
buildGroupsArray(app); // in this function get app as parameter or use UiApp.getActiveApplication();
addCheckBoxesToUI(app);// in this function get app as parameter or use UiApp.getActiveApplication();
var scrollPanel = app.createScrollPanel().setHeight(460);
//...
//...
return app;
}
function respondToSearch(e){
//...
//...
var app = UiApp.getActiveApplication();
var rightPanel = app.getElementById('rightPanel');
for(j in positiveList[0]){
var name = positiveList[0][j];
var id = positiveList[1][j];
Logger.log(name);
Logger.log(id)
var anchor = app.createAnchor(name,'https://ww......folderviewid='+id);
rightPanel.add(anchor)
}
return app;
}

Kendo UI: TreeView - How to tell if a given node has parents

I am trying to use the dragend event on a TreeView in order to send a command to the server in order to make the appropriate change, however, to do this I need parent information on both the target and destination nodes. Currently I have the following:
function dragEndEvent(e) {
var treeViewData = $(".hierarchy-tree").data('kendoTreeView');
var quotaSetID = $("#quotaset-id").val();
var columnID = $("#treeViewColumnID").val();
var targetNode = treeViewData.dataItem(e.sourceNode);
var targetParentNode = targetNode.parent();
var destinationNode = treeViewData.dataItem(e.destinationNode);
var destinationParentNode = null;
if(destinationNode!=null )
destinationParentNode = destinationNode.parent();
var targetName = targetNode.text;
var targetID = targetNode.id;
var targetsParentID = null;
if (targetParentNode != null && targetParentNode.length == 1)
targetsParentID = targetParentNode[0].id;
var destinationName = null;
var destinationID = null;
var destinationsParentID = null;
if (destinationNode != null) {
destinationName = destinationNode.text;
destinationID = destinationNode.id;
if (destinationParentNode != null && destinationParentNode.length == 1)
destinationsParentID = destinationParentNode[0].id;
}
// Followed by ajax query
}
What I have noticed is that the parent() call returns a list and it doesn't seem to me to have any indication of the actual parent. Perhaps I am catching the wrong event, but here, the parent() function seems to return the siblings of the target node. I would also like to be able to tell if the node doesn't have a parent (ie it is at the root level)
Use parentNode(), because parent() returns the array which holds this dataItem as you noticed.
I don't see the parentNode() method either. To tell if a node is a top level node in the dragend event you can just use jquery. You can use jquery to grab the UID as well, which then you can use to get the data node.
var targetNode = e.destinationNode;
if ($(targetNode).parent("ul").parent(".k-treeview").length === 1) {
//top level node
} else {
//not top level node
var htmlNode = $(targetNode).parent("ul").parent(".k-item");
//if you need the telerik version of the node
var treeViewData = $(".hierarchy-tree").data('kendoTreeView');
var parentUid = $(htmlNode).data("uid");
var parentNode = treeViewData.findByUid(parentUid);
var parentDataNode = treeViewData.dataItem(parentNode);
var parentid = parentDataNode.id;
}

Performance difference when reading/writing many files with EPPlus versus Spreadsheet Gear

I've made a simple performance test between EPPlus and Spreadsheet Gear to see if there is any significant difference that would justify buying Spreadsheet Gear.
I am no expert at either application so it's possible the tests aren't written the most efficient way.
The test does the following:
1. Opens an existing Excel-file with 1000 rows and 3 columns. Saves the three values into an entity that is saved into a List<>.
2. Open a new Excel-object
3. Create a header row (bold) with the title of each column.
4. Write back the 1000 entities.
5. Save the new Excelfile.
If I run this test once EPPlus comes out the winner (approx times are EPPlus = 280ms, SG = 500ms). If I run the test 10 times in a row instead (a for-loop opening, copying, saving 10 seperate times) Spreadsheet Gear is faster instead (approx times per file: EPPlus = 165ms, SG = 95ms). For 20 tests the approx times are EPPlus = 160ms / file and SG = 60ms / file.
It seems like (to a certain extent at least) Spreadsheet Gears gets faster and faster the more files I create.
Could anyone explain why EPPlus is the slower one when running consecutive tests? And can I make changes to the code to change this?
EPPlus test function:
var timer = new Stopwatch();
timer.Start();
var data = new List<Item>();
using (var excelIn = new ExcelPackage(new FileInfo(folder + fileIn)))
{
var sheet = excelIn.Workbook.Worksheets[1];
var row = 2;
while (sheet.Cells[row, 1].Value != null)
{
data.Add(new Item()
{
Id = int.Parse(sheet.Cells[row, 1].Text),
Title = sheet.Cells[row, 2].Text,
Value = int.Parse(sheet.Cells[row, 3].Text)
});
row++;
}
}
using (var excelOut = new ExcelPackage())
{
var sheet = excelOut.Workbook.Worksheets.Add("Out");
sheet.Cells.LoadFromCollection(data);
sheet.InsertRow(1, 1);
sheet.Cells[1, 1, 1, 3].Style.Font.Bold = true;
sheet.Cells[1, 1].Value = "Id";
sheet.Cells[1, 2].Value = "Title";
sheet.Cells[1, 3].Value = "Value";
excelOut.SaveAs(new FileInfo(folder + "EPPlus_" + Guid.NewGuid() + ".xlsx"));
}
timer.Stop();
return timer.ElapsedMilliseconds;
Spreadsheet Gear:
var timer = new Stopwatch();
timer.Start();
var data = new List<Item>();
var excelIn = Factory.GetWorkbook(folder + fileIn);
var sheetIn = excelIn.Worksheets[0];
var rowIn = 1;
while (sheetIn.Cells[rowIn, 0].Value != null)
{
data.Add(new Item()
{
Id = int.Parse(sheetIn.Cells[rowIn, 0].Text),
Title = sheetIn.Cells[rowIn, 1].Text,
Value = int.Parse(sheetIn.Cells[rowIn, 2].Text)
});
rowIn++;
}
excelIn.Close();
var excelOut = Factory.GetWorkbook();
var sheetOut = excelOut.Worksheets.Add();
sheetOut.Name = "Out";
var rowOut = 0;
sheetOut.Cells[rowOut, 0, rowOut, 2].Font.Bold = true;
sheetOut.Cells[rowOut, 0].Value = "Id";
sheetOut.Cells[rowOut, 1].Value = "Title";
sheetOut.Cells[rowOut++, 2].Value = "Value";
foreach (var item in data)
{
sheetOut.Cells[rowOut, 0].Value = item.Id;
sheetOut.Cells[rowOut, 1].Value = item.Title;
sheetOut.Cells[rowOut++, 2].Value = item.Value;
}
excelOut.SaveAs(folder + "SpreadsheetGear_" + Guid.NewGuid() + ".xlsx", FileFormat.OpenXMLWorkbook);
excelOut.Close();
timer.Stop();
return timer.ElapsedMilliseconds;
Main function
var runs = 1;
var testerG = new TestSpreadsheetGear();
var testerE = new TestEpPlus();
var msE = 0.0;
var msG = 0.0;
var i = 0;
for (i = 0; i < runs; ++i)
{
msG += new TestSpreadsheetGear().Run(folder, originalFile);
}
for(i = 0; i < runs; ++i)
{
msE += new TestEpPlus().Run(folder, originalFile);
}
Console.WriteLine("Spreadsheet time: " + msG + ". Per file: " + msG / runs);
Console.WriteLine("EP Plus time: " + msE + ". Per file: " + msE / runs);
Console.ReadKey();
I believe that the reason for the results you are seeing is the fact that on the first run the .NET CLR must JIT the code. Since SpreadsheetGear is a complete spreadsheet engine under the hood (as opposed to a read / write library) there is more code to JIT - thus the first run is taking longer for SpreadsheetGear than EPPlus (I am speculating here but have a great deal of experience in benchmarking .NET code over the last 10 years).
I do not have EPPlus installed but I did write a test which tries to do the same thing you are doing. with SpreadsheetGear 2012 Since I don't have your starting workbook I first build the workbook. Then, I used more optimal SpreadsheetGear APIs. The first time I run I get 141 milliseconds for SpreadsheetGear 2012. After the first run I get 9 or 10 milliseconds for each run on an overclocked Core i7-980x running Win7 x86 and a release build run without debugger.
I have pasted my code below (just paste it into a .NET 4.0 C# console application).
One more thought I have is that this is a very small test case. To really see the performance of SpreadsheetGear 2012 try this with 100,000 rows or even 1 million rows.
Disclaimer: I own SpreadsheetGear LLC
using System;
using System.Collections.Generic;
using System.Diagnostics;
using SpreadsheetGear;
namespace SGvsEPPlus
{
class Program
{
internal struct Item
{
internal Item(int id, string title, int value)
{
Id = id;
Title = title;
Value = value;
}
internal int Id;
internal string Title;
internal int Value;
}
static void Test(int rows)
{
string filename = #"C:\tmp\MyWorkbook.xlsx";
Console.Write("Test({0})...", rows);
var timer = new Stopwatch();
// Create workbook since we don't have poster's original workbook.
timer.Restart();
var workbook = Factory.GetWorkbook();
var values = (SpreadsheetGear.Advanced.Cells.IValues)workbook.Worksheets[0];
for (int row = 1; row <= rows; row++)
{
values.SetNumber(row, 0, row);
values.SetText(row, 1, "Title " + row);
values.SetNumber(row, 2, row * 10);
}
Console.Write("Create workbook={0:0}...", timer.Elapsed.TotalMilliseconds);
// Save workbook
timer.Restart();
workbook.SaveAs(filename, FileFormat.OpenXMLWorkbook);
Console.Write("Save workbook={0:0}...", timer.Elapsed.TotalMilliseconds);
// Track total time of original test.
var totalTimer = Stopwatch.StartNew();
// Open workbook
timer.Restart();
var excelIn = Factory.GetWorkbook(filename);
Console.Write("Open excelIn={0:0}...", timer.Elapsed.TotalMilliseconds);
// Copy workbook to list
timer.Restart();
var sheetIn = excelIn.Worksheets[0];
var valuesIn = (SpreadsheetGear.Advanced.Cells.IValues)sheetIn;
var rowIn = 1;
var data = new List<Item>(rows);
while (valuesIn[rowIn, 0] != null)
{
data.Add(new Item(
(int)valuesIn[rowIn, 0].Number,
valuesIn[rowIn, 1].Text,
(int)valuesIn[rowIn, 2].Number));
rowIn++;
}
excelIn.Close(); // Not necessary but left for consistency.
Console.Write("excelIn->data={0:0}...", timer.Elapsed.TotalMilliseconds);
timer.Restart();
var excelOut = Factory.GetWorkbook();
var sheetOut = excelOut.Worksheets[0];
var valuesOut = (SpreadsheetGear.Advanced.Cells.IValues)sheetOut;
sheetOut.Name = "Out";
var rowOut = 0;
sheetOut.Cells[rowOut, 0, rowOut, 2].Font.Bold = true;
sheetOut.Cells[rowOut, 0].Value = "Id";
sheetOut.Cells[rowOut, 1].Value = "Title";
sheetOut.Cells[rowOut++, 2].Value = "Value";
foreach (var item in data)
{
valuesOut.SetNumber(rowOut, 0, item.Id);
valuesOut.SetText(rowOut, 1, item.Title);
valuesOut.SetNumber(rowOut, 2, item.Value);
rowOut++;
}
Console.Write("data->excelOut={0:0}...", timer.Elapsed.TotalMilliseconds);
timer.Restart();
excelOut.SaveAs(#"C:\tmp\SpreadsheetGear_" + Guid.NewGuid() + ".xlsx", FileFormat.OpenXMLWorkbook);
excelOut.Close(); // Again - not necessary.
Console.WriteLine("Save excelOut={0:0}...", timer.Elapsed.TotalMilliseconds);
Console.WriteLine(" Total={0:0}", totalTimer.Elapsed.TotalMilliseconds);
}
static void Main(string[] args)
{
// Do it three times with 1000 rows. Note that the first
// time takes longer because code must be JITted.
Test(1000);
Test(1000);
Test(1000);
}
}
}

Resources