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 つのアプリケーションを異なるサーバーにデプロイします。
- サーバーでメモリプロファイラーを実行する