我有许多设置了 SessionStateBehavior.ReadOnly 的控制器,以启用多个 ajax 请求的并行处理。
但是我注意到这并不能阻止我写入会话(与 SessionStateBehavior.Disabled 不同,它会引发异常)。
我的应用程序使用 Microsoft Charting,并且图表和图像映射是响应 ajax 请求生成的,图表存储在会话中,直到浏览器呈现图像标记,此时图像 src 触发浏览器请求图表图像从会话中检索。因此,在写入之前尝试从会话中读取没有问题。
一切正常,我有多个 ajax 请求被并行处理,并且我的图表被正确返回,所以我在写入会话时声明 SessionStateBehavior.ReadOnly 是否重要?
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using System.Web.UI.DataVisualization.Charting;
using Mdl.Rcm.Web.Areas.Dashboards.Models;
using Mdl.Web.Security;
using Mdl.Persistence.UoW;
using Mdl.Rcm.Business.Dashboards;
using Mdl.Rcm.Business.Dashboards.Models;
using Mdl.Rcm.Web.Areas.Dashboards.Charts;
using Mdl.Rcm.Web.Areas.Dashboards.Charts.OrganisationConnectionsByRole;
using Mdl.Web.Mvc;
namespace Mdl.Rcm.Web.Areas.Dashboards.Controllers
{
/// <summary>
/// Controller for the Organisation Connections By Role chart.
/// </summary>
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
[RedirectingAuthorize(Roles = "Dashboards")]
[Area(IsSiteRoot = false)]
public class ChartOrganisationConnectionsByRoleController : Controller
{
private readonly IRelationshipAndRiskService relationshipAndRiskService;
private readonly IConfigurationService configurationService;
private readonly IOrganisationConnectionsByRoleChartBuilder chartBuilder;
private readonly IListService listService;
private BarConfiguration barConfiguration;
private const string TempDataKey = "OrganisationConnectionsByRoleData";
public ChartOrganisationConnectionsByRoleController(IOrganisationConnectionsByRoleChart organisationConnectionsByRoleChart, IRelationshipAndRiskService relationshipAndRiskService, IConfigurationService configurationService, IOrganisationConnectionsByRoleChartBuilder chartBuilder, IListService listService)
{
this.relationshipAndRiskService = relationshipAndRiskService;
this.configurationService = configurationService;
this.chartBuilder = chartBuilder;
this.listService = listService;
}
/// <summary>
/// Standard Organisation Connections by Role component loader
/// </summary>
/// <param name="listId">The list id.</param>
/// <param name="degree">The active degree.</param>
/// <param name="barIndex">Index of the active bar.</param>
/// <returns></returns>
[HandleAjaxError]
public ActionResult Load(int listId, int degree, int barIndex)
{
using (UnitOfWork.Start("Analysis"))
{
// Get the data
var relationshipPlanningData = GetChartDataForInitialLoadParentComponent(listId);
var connectionsFound = relationshipPlanningData.SeriesModels.Count > 0;
var listName = listService.GetListName(listId).Name;
var information = new InformationModel(degree, true) { ConnectionsFound = connectionsFound, NumberOfOrganisationsInList = relationshipPlanningData.TotalNumberOfOrganisations, ListName = listName };
return PartialView("OrganisationConnectionsByRoleComponent", new OrganisationConnectionsByRoleComponentModel { ListId = listId, ActiveDegree = degree, ActiveBarIndex = barIndex, BarConfiguration = configurationService.GetBarConfiguration(), ConnectionsFound = connectionsFound, Information = information});
}
}
/// <summary>
/// Creates the Connections by Role chart and stores it in Session then returns the chart map.
/// The chart image map is always created first, the chart is created as part of the process of creating the map.
/// </summary>
/// <returns></returns>
public ActionResult Chart(Guid chartId, int listId)
{
using (UnitOfWork.Start("Analysis"))
{
barConfiguration = configurationService.GetBarConfiguration();
var relationshipPlanningData = GetChartDataForInitialLoadChart();
if (relationshipPlanningData.SeriesModels.Count == 0)
{
return PartialView("InfoMessage", "No connections found");
}
// Set up the chart
return GenerateChart(relationshipPlanningData, listId, chartId);
}
}
private ActionResult GenerateChart(OrganisationConnectionsByRoleData relationshipPlanningData, int listId, Guid chartId)
{
var chartAndXPoints = chartBuilder.BuildChart(2, relationshipPlanningData, barConfiguration, SetActiveBarIndex(-1), listId);
// Store the chart in session for retrieval by the browser as the src on an image tag.
ChartSession.GenerateChartToSession(chartId, chartAndXPoints.Chart, Session);
// Get y data for use client side
var yPointsDataView = GetYPointsDataView(relationshipPlanningData);
// Get x co-ordinates for use client side. Must be done after the chart has been generated to session.
var xPointsView = GetXPointsView(chartAndXPoints.XPoints);
// Render the image tag and image map
return this.Chart(chartAndXPoints.Chart, xPointsView + yPointsDataView, chartId, "ConnectionsByRoleImage");
}
/// <summary>
/// Gets the chart data for use by the main component.
/// </summary>
/// <param name="listId">The list id.</param>
/// <returns></returns>
private OrganisationConnectionsByRoleData GetChartDataForInitialLoadParentComponent(int listId)
{
// This is the call from the load action so get the data and store it for use by the chart action to save the performance hit
var data = relationshipAndRiskService.GetOrganisationConnectionsByRoleChartData(listId);
TempData[TempDataKey] = data;
return data;
}
/// <summary>
/// Gets the chart data for use by the main component chart.
/// </summary>
/// <returns></returns>
private OrganisationConnectionsByRoleData GetChartDataForInitialLoadChart()
{
// This call is from the chart action so use the data that was retreived on the load action
return (OrganisationConnectionsByRoleData)TempData[TempDataKey];
}
/// <summary>
/// Return the Connections By Role chart from session.
/// </summary>
/// <returns></returns>
public ActionResult ConnectionsByRoleImage(Guid chartId)
{
var chart = ChartSession.GetChartFromSession(chartId, Session);
return File(chart, "image");
}
/// <summary>
/// Return the Connections By Role chart from session.
/// </summary>
/// <param name="listId">The list id.</param>
/// <param name="degree">The active degree.</param>
/// <param name="barIndex">Index of the active bar.</param>
/// <returns></returns>
public ActionResult ConnectionsByRoleActive(int listId, int degree, int barIndex)
{
using (UnitOfWork.Start("Analysis"))
{
barConfiguration = configurationService.GetBarConfiguration();
// Get the data
var relationshipPlanningData = relationshipAndRiskService.GetOrganisationConnectionsByRoleChartData(listId);
if (relationshipPlanningData.SeriesModels.Count == 0)
{
return PartialView("InfoMessage", "No connections found");
}
// Set up the chart
var chartAndXPoints = chartBuilder.BuildChart(SetActiveDegree(degree), relationshipPlanningData, barConfiguration, SetActiveBarIndex(-1), listId);
var ms = new MemoryStream();
chartAndXPoints.Chart.SaveImage(ms, ChartImageFormat.Png);
return File(ms.ToArray(), "image");
}
}
/// <summary>
/// Gets the graph X points and render them as a javascript object for use by the view.
/// </summary>
/// <param name="xPoints">The x points.</param>
/// <returns></returns>
private string GetXPointsView(List<float> xPoints)
{
var model = new XPointsModel
{
ChartName = "Connections By Role",
Points = xPoints
};
return this.ViewAsString("XPoints", model);
}
/// <summary>
/// Gets the Y points data and render them as a javascript object for use by the view.
/// </summary>
/// <param name="relationshipPlanningData">The relationship planning data.</param>
/// <returns></returns>
private string GetYPointsDataView(OrganisationConnectionsByRoleData relationshipPlanningData)
{
var degree1DataPoints = relationshipPlanningData.SeriesModels[0].DataPoints.Select(p => p.Y).ToList();
var degree2DataPoints = relationshipPlanningData.SeriesModels[1].DataPoints.Select(p => p.Y).ToList();
var model = new YPointsDegree1And2Model
{
ChartName = "Connections By Role",
Degree1Data = degree1DataPoints,
Degree2Data = degree2DataPoints
};
return this.ViewAsString("YPointsDegree1And2", model);
}
private int SetActiveBarIndex(int barIndex)
{
if (barIndex == -1)
{
barIndex = barConfiguration.Bars.First(b => b.IsDefaultActive).Index;
}
return barIndex;
}
private static int SetActiveDegree(int degree)
{
if (degree == -1)
{
degree = 2;
}
return degree;
}
}
}
public class ChartSession
{
public static byte[] GetChartFromSession(Guid chartId, HttpSessionStateBase session)
{
// Get the chart from session
var data = session[chartId.ToString()] as byte[];
// Clear the session
session[chartId.ToString()] = null;
return data;
}
public static void GenerateChartToSession(Guid chartId, Chart chart, HttpSessionStateBase session)
{
var ms = new MemoryStream();
chart.SaveImage(ms, ChartImageFormat.Png);
session[chartId.ToString()] = ms.ToArray();
}