Reputation: 129
Issue :
I am having Kendo Grid in my Angular JS html page. Kendo Grid data is coming from my remote service Web API.
Kendo grid try to download content of 38 MB in browser for each 10 records while we are doing Paging (or page load on first time) and it takes approx. 6 minutes to load data.
What and which content it is downloading for 38 MB ?
What i already implemented by reading/learning similar support tickets on stack overflow :
Implemented Server Paging true (pageSize = 10, Total records = 56000 approx.)
Bundling of JS and CSS
I tried both below options:
scrollable: { virtual: true } OR scrollable: { endless: true }
I check my stored procedure on production, it executes in less than 3 seconds for approx. 55000 records. (on production and staging both servers).
I check my Web API controller, it returns response in less than 4 seconds to kendo grid, and then kendo grid takes too much time to populate data.
I have below JS and CSS for Kendo (already implemented bundling) :
Below are my live project pages :
HTML Page :
<div id="heatMapGrid" kendo-grid k-options="vm.heatMapGridOptions"></div>
AngularJS Controller :
var dataSourceHeatMapGrid = new kendo.data.DataSource({
transport: {
read: function (options) {
heatMapService.getHeatMapGrid(options.data, heatMapGridParams)
.then(function (response) {
options.success(response.data);
$rootScope.optioncallback = options;
//$scope.htmapGridCSV = [];
//$scope.htmapGridCSV = response.data.exportData;
}).catch(function (e) {
console.log('Error: ', e);
throw e;
});
},
parameterMap: function (options) {
return JSON.stringify(options);
}
},
schema: {
data: function (response) {
return response.gridData;
},
total: function (response) {
return response.Total;
},
model: {
fields: {
TPID: { type: "number" },
TPName: { type: "string" },
EndCustomerPurchaseAmt: { type: "number" },
PrimaryExpirationMonth: { type: "string" },
AgreementID: { type: "number" },
TotalPurchased: { type: "number" },
TotalAssigned: { type: "number" },
OverUnder: { type: "number" },
VSEntPurchasedUnits: { type: "number" },
VSProMSDNUnits: { type: "number" },
VSTestProMSDNUnits: { type: "number" },
MSDNPlat: { type: "number" },
CloudPurchasedUnits: { type: "number" },
UnbilledOverage: { type: "number" },
AzurePotentialRevenue: { type: "number" }
}
}
},
pageSize: 10,
serverPaging: true
});
vm.heatMapGridOptions = {
columns: [
{ "title": "", template: "<a title='#=TPID#' #=isPinnedAccount==1 ? \"class='terrunpinaccount'\" : \"class='terrpinaccount'\"# ng-click='vm.pinUnpinAccount(\"#=TPID#\")'></a>" },
{ "title": "Account Name", "field": "TPName", template: "<a href='javascript:void(0);' ng-click='vm.tPIDDetails(\"#=TPID#\",\"#=TPName#\")' title='#=TPName#'><div class='DisplayTitleTPName'>#=TPName#<ul><li>AM: #=AM#, OM: #=OperatingModel#, Country: #=Country#</li><li>DevSales Lead: #=SalesLead#, SSP: #=Dev_SSP#, TSP: #=DevTSP#</li></ul></div></a>", headerAttributes: { style: "white-space: normal; overflow: visible;" } },
{
"title": "PERFORMANCE AND ANNIVERSARIES", headerAttributes: { style: "text-align: center;font-weight: bold;" },
columns:
[{
"title": "Renewals and True Ups", headerAttributes: { style: "text-align: center;font-weight: bold;" },
columns: [{ "title": "Total Annualized Expiring", "field": "EndCustomerPurchaseAmt", format: "{0:c0}", headerAttributes: { style: "white-space: normal; overflow: visible;" } },
{ "title": "Primary Anniversary Month", "field": "PrimaryExpirationMonth", headerAttributes: { style: "white-space: normal; overflow: visible;" } },
{ "title": "Agreement Number", "field": "AgreementID", format: "{0:n0}", headerAttributes: { style: "white-space: normal; overflow: visible;" } }]
}]
},
{
"title": "EFFECTIVE LICENSE POSITIONS", headerAttributes: { style: "text-align: center;font-weight: bold;" },
columns:
[{
"title": "Visual Studio Subscriptions", headerAttributes: { style: "text-align: center;font-weight: bold;" },
columns: [{ "title": "Purchased", "field": "TotalPurchased", format: "{0:n0}" },
{ "title": "Assigned", "field": "TotalAssigned", format: "{0:n0}" },
{ "title": "Over Under", "field": "OverUnder", format: "{0:n0}" }]
},
{
"title": "Account Footprint (Active SA Licenses)", headerAttributes: { style: "text-align: center;font-weight: bold;" },
columns: [{ "title": "Enterprise w/ MSDN", "field": "VSEntPurchasedUnits", format: "{0:n0}", headerAttributes: { style: "white-space: normal; overflow: visible;" } },
{ "title": "Pro w/ MSDN", "field": "VSProMSDNUnits", format: "{0:n0}", headerAttributes: { style: "white-space: normal; overflow: visible;" } },
{ "title": "Test Pro w/ MSDN", "field": "VSTestProMSDNUnits", format: "{0:n0}", headerAttributes: { style: "white-space: normal; overflow: visible;" } },
{ "title": "MSDN Platforms", "field": "MSDNPlat", format: "{0:n0}", headerAttributes: { style: "white-space: normal; overflow: visible;" } },
{ "title": "Cloud", "field": "CloudPurchasedUnits", format: "{0:n0}" }]
},
{
"title": "Azure", headerAttributes: { style: "text-align: center;font-weight: bold;" },
columns: [{ "title": "Unbilled Overage", "field": "UnbilledOverage", format: "{0:c0}", headerAttributes: { style: "white-space: normal; overflow: visible;" } },
{ "title": "Potential Revenue", "field": "AzurePotentialRevenue", headerTemplate: '<span title="Potential Revue is based on the delta of activated seats and <br/> developers deploying to Azure multiplied by the annual <br/> value of an Azure attached developer ($15k)">Potential Revenue</span>', format: "{0:c0}", headerAttributes: { style: "white-space: normal; overflow: visible;" } }]
}]
}
],
groupable: false,
sortable: true,
resizable: true,
//pageable: true,
pageable: {
refresh: true,
pageSizes: [10, 20, 50],
},
columnMenu: true,
scrollable: false
//filterable: true
};
$("#heatMapGrid").data("kendoGrid").setDataSource(dataSourceHeatMapGrid);
AngularJS Service :
services.getHeatMapGrid = function (command, heatMapGridParams) {
var data = {
page: command.page,
pageSize: command.pageSize,
skip: command.skip,
take: command.take,
alias: heatMapGridParams.alias,
hasDevTest: heatMapGridParams.hasDevTest,
hasDevTestLabs: heatMapGridParams.hasDevTestLabs,
hasXamarin: heatMapGridParams.hasXamarin,
devOpsMSShopFlag: heatMapGridParams.devOpsMSShopFlag,
devOpsOSSThirdPartyShopsFlag: heatMapGridParams.devOpsOSSThirdPartyShopsFlag,
intelligentAppsFlag: heatMapGridParams.intelligentAppsFlag,
paaSServicesFlag: heatMapGridParams.paaSServicesFlag,
enterpriseStepUpFlag: heatMapGridParams.enterpriseStepUpFlag,
devsLearningAzureFlag: heatMapGridParams.devsLearningAzureFlag,
devOpsAcceleratorEligibleFlag: heatMapGridParams.devOpsAcceleratorEligibleFlag,
overAssignedFlag: heatMapGridParams.overAssignedFlag,
lowAssignmentsFlag: heatMapGridParams.lowAssignmentsFlag,
hasCloudSubscriptionFlag: heatMapGridParams.hasCloudSubscriptionFlag,
hasUnbilledOverageFlag: heatMapGridParams.hasUnbilledOverageFlag,
hasDevTestOppty: heatMapGridParams.hasDevTestOppty,
areaID: heatMapGridParams.areaID,
countryID: heatMapGridParams.countryID,
segmentID: heatMapGridParams.segmentID,
subsegmentID: heatMapGridParams.subsegmentID,
salesUnitID: heatMapGridParams.salesUnitID,
agreementRenewalOrTrueupID: heatMapGridParams.agreementRenewalOrTrueupID,
aM: heatMapGridParams.aM,
industry: heatMapGridParams.industry,
hasAppServOppty: heatMapGridParams.hasAppServOppty,
hasDotNetDeveloperFlag: heatMapGridParams.hasDotNetDeveloperFlag,
paaSReadyFlag: heatMapGridParams.paaSReadyFlag,
startMonth: heatMapGridParams.startMonth,
endMonth: heatMapGridParams.endMonth
};
return $http({ method: 'GET', url: config.apiUrl + 'Account/HeatMapGrid/', params: data });
};
Web API :
[HttpGet]
public heatMapGridAndExport HeatMapGrid([FromUri]HeatMapGridModel model)
{
ListView listView = new ListView();
List<getHeatMapDataGlobalFilter_Result> listGridDataForTotalCount = new List<getHeatMapDataGlobalFilter_Result>();
listGridDataForTotalCount = listView.GetListViewGridData(model.alias, model.hasDevTest, model.hasDevTestLabs, model.hasXamarin, model.devOpsMSShopFlag, model.devOpsOSSThirdPartyShopsFlag, model.intelligentAppsFlag, model.paaSServicesFlag, model.enterpriseStepUpFlag, model.devsLearningAzureFlag, model.devOpsAcceleratorEligibleFlag, model.overAssignedFlag, model.lowAssignmentsFlag, model.hasCloudSubscriptionFlag, model.hasUnbilledOverageFlag, model.hasDevTestOppty, model.areaID, model.countryID, model.segmentID, model.subsegmentID, model.salesUnitID, model.agreementRenewalOrTrueupID, model.aM, model.industry, model.hasAppServOppty, model.hasDotNetDeveloperFlag, model.paaSReadyFlag, model.startMonth, model.endMonth);
List<getHeatMapDataGlobalFilter_Result> listGridData = new List<getHeatMapDataGlobalFilter_Result>();
listGridData = listGridDataForTotalCount.Skip(model.skip).Take(model.take).OrderByDescending(c => c.EndCustomerPurchaseAmt).ToList();
//List<heatMapExport> listExportData = new List<heatMapExport>();
//listExportData = listGridDataForTotalCount.Select(c => new heatMapExport()
//{
// TPName = c.TPName,
// TPID = c.TPID,
// OperatingModel = c.OperatingModel,
// Area = c.Area,
// Country = c.Country,
// CreditedRegion = c.CreditedRegion,
// CreditedDistrict = c.CreditedDistrict,
// Segment = c.Segment,
// ATUManager = c.ATUManager,
// Dev_SSP = c.Dev_SSP,
// AM = c.AM,
// Industry = c.Industry,
// ATSName = c.ATSName,
// AssignedPect = string.Format("{0:p0}", c.AssignedPect),
// ActivatedPect = string.Format("{0:p0}", c.ActivatedPect),
// AzureActivated = Convert.ToString(c.AzureActivated),
// EndCustomerPurchaseAmt = string.Format("{0:c0}", c.EndCustomerPurchaseAmt),
// PrimaryExpirationMonth = Convert.ToString(c.PrimaryExpirationMonth),
// AgreementID = Convert.ToString(c.AgreementID),
// TotalPurchased = string.Format("{0:n0}", c.TotalPurchased),
// TotalAssigned = string.Format("{0:n0}", c.TotalAssigned),
// OverUnder = string.Format("{0:n0}", c.OverUnder),
// VSEntPurchasedUnits = string.Format("{0:n0}", c.VSEntPurchasedUnits),
// VSProMSDNUnits = string.Format("{0:n0}", c.VSProMSDNUnits),
// VSTestProMSDNUnits = string.Format("{0:n0}", c.VSTestProMSDNUnits),
// MSDNPlat = string.Format("{0:n0}", c.MSDNPlat),
// CloudPurchasedUnits = string.Format("{0:n0}", c.CloudPurchasedUnits),
// UnbilledOverage = string.Format("{0:c0}", c.UnbilledOverage),
// AzurePotentialRevenue = string.Format("{0:c0}", c.AzurePotentialRevenue)
//}).ToList();
var heatMapData = new heatMapGridAndExport
{
gridData = listGridData,
//exportData = listExportData,
Total = listGridDataForTotalCount.Count()
};
return heatMapData;
}
My Environment :
Version Telerik Control - Kendo UI v2017.2.621
Operating System Development machine - Windows 10 Enterprise (8 GB RAM, Intel Core i7 Processor, 64 bit) (Client OS)
Browser - Google Chrome, Version 65.0.3325.181
.NET Framework - Version 4.6.1
Visual Studio - Enterprise 2015, Version 14.0.25431.01 (Update 3)
Coding Language - C#
Here is my browser screen shot for production server :
Here is the another screenshot when i am clicking on 2nd page, it again download 38 mb content and it takes approx. 6 minutes. (server paging = true and pageSize = 10)
Code Debugging screenshots:
What i am doing wrong? Can any one please help me.
Thank you in advance.
Upvotes: 1
Views: 1402
Reputation: 1026
Can you disable the line where you attach the ~56K rows of export data to the WebAPI response and see how it performs? I suspect that's your problem
var heatMapData = new heatMapGridAndExport
{
gridData = listGridData,
//exportData = listExportData, //perhaps make conditional, for export only?
Total = listGridDataForTotalCount.Count()
};
return heatMapData;
Edit: Since that doesn't seem to have fixed your problem, can you please try re-ordering the Linq method calls like this, as the behaviour you're getting implies the entire result set is coming back?
listGridData =
listGridDataForTotalCount.OrderByDescending(c => c.EndCustomerPurchaseAmt)
.Skip(model.skip).Take(model.take).ToList();
I don't know for certain, but I wonder if having the OrderByDescending
last forces Linq to go back to the entire resultset and that's what you end up getting from the ToList
?
Upvotes: 1