在 Asp.net MVC 应用程序中,我们有一个页面可以下载动态生成的 Excel 报告。客户端应用程序调用生成 excel 文件并将文件名返回给客户端的 WCF 服务。WCF 服务使用 OpenXML Sax Approach 来生成 excel 文件。
服务器调用存储过程并使用数据读取器来获取数据。通常该文件包含 10000 条记录。我们在测试环境中没有遇到任何性能问题。在生产环境中,如果 10 个人访问该报告,服务器内存将达到最大值,CPU 利用率也为 98%。因此,它会给该服务器中的所有应用程序带来问题。服务器只有 4GB RAM。我运行 4 个应用程序。通常我的应用程序会占用更多内存。
这是代码:
public string GetMemberRosterHistoryFile(string VendorId, string versionId, DateTime FromDate, string ActionIndicator)
{
string path = ConfigurationManager.AppSettings["FilePath"] + Guid.NewGuid() + ".xlsx";
try
{
path = PathInfo.GetPath(path);
log4net.ThreadContext.Properties["MethodName"] = "GetMemberRostersHistory";
log.Info("Getting member rosters History");
string sConn = ConfigurationManager.ConnectionStrings["VendorConnectContext"].ConnectionString;
using (SqlConnection oConn = new SqlConnection(sConn))
{
oConn.Open();
log.Debug("Connected");
string sCmd = "pGetMemberRostersHistory";
SqlCommand oCmd = new SqlCommand(sCmd, oConn);
oCmd.CommandTimeout = Int32.MaxValue;
oCmd.CommandType=CommandType.StoredProcedure;
oCmd.Parameters.AddWithValue("@FromDate", FromDate.ToShortDateString());
oCmd.Parameters.AddWithValue("@ActionIndicator", ActionIndicator);
int index=1;
StringBuilder programs = new StringBuilder();
if (string.IsNullOrEmpty(versionId))
{
foreach (string value in GetPrograms(VendorId))
{
if (index > 1)
{
programs.Append(",");
}
programs.Append(value);
index++;
}
}
else
{
foreach (string value in GetPrograms(VendorId, versionId))
{
if (index > 1)
{
programs.Append(",");
}
programs.Append(value);
index++;
}
}
oCmd.Parameters.AddWithValue("@ProgramsList", programs.ToString());
string[] FieldNames = new string[]
{
"ActionIndicator",
"ChangeNotes",
"ActionEffectiveDate",
"MembershipTerminationDate",
"GPOId",
"GLN",
"HIN",
"Name1",
"Name2",
"AddressType",
"Address1",
"Address2",
"Address3",
"City",
"StateProvince",
"ZipPostalCode",
"Country",
"PhoneNbr",
"FaxNbr",
"RelationshipToGPO",
"RelationshipToDirectParent",
"DirectParentGPOId",
"DirectParentName1",
"TopParentGPOId",
"TopParentName1",
"MemberStatus",
"MembershipStartDate",
"OrganizationalStatus",
"ClassOfTradeName",
"DEA",
"DSHorHRSA",
"PHEffectiveDate",
"PHExpirationDate",
"BLPHEffectiveDate",
"BLPHExpirationDate",
"MMEffectiveDate",
"MMExpirationDate",
"BLMMEffectiveDate",
"BLMMExpirationDate",
"DIEffectiveDate",
"DIExpirationDate",
"LBEffectiveDate",
"LBExpirationDate",
"NMEffectiveDate",
"NMExpirationDate"
,"BLMemberId"
,"GPOCorporateGroup"
,"GPOAffiliateGroup"
,"GPO2AffiliateGroup"
,"GPORelatedGroup"
,"GPOIDNGroup"
};
string[] columnNames = new string[]
{
"Action Indicator",
"Change Notes",
"Action Effective Date",
"Membership Termination Date",
"GPO ID",
"GLN",
"Health Industry Number (HIN)",
"Name 1",
"Name 2",
"Address Type",
"Address 1",
"Address 2",
"Address 3",
"City",
"State/Province",
"Postal Code",
"Country",
"Phone",
"Fax",
"Relationship to GPO",
"Relationship to Direct Parent",
"Direct Parent GPO ID",
"Direct Parent Name 1",
"Top Parent GPO ID",
"Top Parent Name 1",
"Member Status",
"Membership Start Date",
"Organizational Status",
"Class of Trade",
"DEA #",
"DSH and/or HRSA Number",
"Pharmacy Start Date",
"Pharmacy End Date",
"BL Pharmacy Start Date",
"BL Pharmacy End Date",
"Med Surg Start Date",
"Med Surg End Date",
"BL Med Surg Start Date",
"BL Med Surg End Date",
"Food Service Start Date",
"Food Service End Date",
"Laboratory Start Date",
"Laboratory End Date",
"NonMedical Start Date",
"NonMedical End Date"
,"Broadlane ID"
,"Corporate Group"
,"Affiliate Group"
,"2nd Affiliate Group"
,"Related Group"
,"IDN Group"
};
//object result = oCmd.ExecuteScalar();
//int count=(result!=null ? (int)result : 0);
//oCmd.CommandText = "pGetMemberRostersHistory";
//oCmd.CommandTimeout = Int32.MaxValue;
using (SqlDataReader oReader = oCmd.ExecuteReader(CommandBehavior.CloseConnection))
{
SAXExcelExporter exporter = new SAXExcelExporter();
exporter.Export(oReader, columnNames, FieldNames, path, "MemberRoster");
}
}
return path;
}
catch (Exception ex)
{
log.Error("In exception", ex);
return null;
}
}
出口代码:
public void Export(SqlDataReader export, string[] columnNames, string[] fieldNames, string filename, string sheetName)
{
Assembly _assembly = Assembly.GetExecutingAssembly();
Stream stream = _assembly.GetManifestResourceStream("MA.VMS.Server.Template.xlsx");
FileStream newfile = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite);
stream.CopyTo(newfile);
stream.Close();
newfile.Close();
using (SpreadsheetDocument myDoc = SpreadsheetDocument.Open(filename, true))
{
WorkbookPart workbookPart = myDoc.WorkbookPart;
WorksheetPart worksheetPart = workbookPart.WorksheetParts.Last();
string origninalSheetId = workbookPart.GetIdOfPart(worksheetPart);
WorksheetPart replacementPart = workbookPart.AddNewPart<WorksheetPart>();
string replacementPartId = workbookPart.GetIdOfPart(replacementPart);
OpenXmlReader reader = OpenXmlReader.Create(worksheetPart);
OpenXmlWriter writer = OpenXmlWriter.Create(replacementPart);
while (reader.Read())
{
if (reader.ElementType == typeof(SheetData))
{
if (reader.IsEndElement)
continue;
writer.WriteStartElement(new SheetData());
Row hr = new Row();
writer.WriteStartElement(hr);
for (int col = 0; col < columnNames.Length; col++)
{
Cell c = new Cell();
c.DataType = CellValues.InlineString;
InlineString iss = new InlineString();
iss.AppendChild(new Text() { Text = columnNames[col] });
c.AppendChild(iss);
writer.WriteElement(c);
}
writer.WriteEndElement();
//for (int row = -1; row < count; row++)
while (export.Read())
{
Row r = new Row();
writer.WriteStartElement(r);
//if (row == -1)
//{
// for (int col = 0; col < columnNames.Length; col++)
// {
// Cell c = new Cell();
// c.DataType = CellValues.InlineString;
// InlineString iss = new InlineString();
// iss.AppendChild(new Text() { Text = columnNames[col] });
// c.AppendChild(iss);
// writer.WriteElement(c);
// }
//}
//else
//{
//export.Read();
for (int col = 0; col < fieldNames.Length; col++)
{
Cell c = new Cell();
c.DataType = CellValues.InlineString;
InlineString iss = new InlineString();
iss.AppendChild(new Text() { Text = GetValue(export[fieldNames[col]]) });
c.AppendChild(iss);
writer.WriteElement(c);
}
//}
writer.WriteEndElement();
}
writer.WriteEndElement();
}
else
{
if (reader.IsStartElement)
{
writer.WriteStartElement(reader);
}
else if (reader.IsEndElement)
{
writer.WriteEndElement();
}
}
}
reader.Close();
writer.Close();
Sheet sheet = workbookPart.Workbook.Descendants<Sheet>().Where(s => s.Id.Value.Equals(origninalSheetId)).First();
sheet.Id.Value = replacementPartId;
workbookPart.DeletePart(worksheetPart);
}
}
我很担心。当我查看 proc 在 26 秒内返回数据时,excel 下载需要 3 分钟以上。
我应该怎么做这种情况?以下是我正在考虑的解决方案:
- 将excel下载异步移动并发送下载链接
- 在不同的服务器上部署 2 个应用程序。
- 在服务器上做内存分析器