Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I still write to session when SessionStateBehavior.ReadOnly is set on a controller and should I care?

I have a number of controllers with SessionStateBehavior.ReadOnly set on them to enable parallel processing of multiple ajax requests.

But I have noticed that this doesn't stop me from writing to session (unlike SessionStateBehavior.Disabled which throws an exception).

My application uses Microsoft Charting and the chart and image map are generated in response to an ajax request with the chart being stored in session until the image markup is rendered by the browser at which point the image src triggers the browser to request the chart image which is retrieved from session. So there are no issues here with attempting to read from session before its been written.

Everything works fine, I have multiple ajax requests being processed in parallel and my charts are being returned correctly so does it matter that I have declared SessionStateBehavior.ReadOnly when I am writing to session?

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();
        }
like image 707
Simon Keep Avatar asked Jan 04 '13 09:01

Simon Keep


1 Answers

SessionStateBehavior only tells ASP.NET what locks to put on the Session

See this question: Controller SessionStateBehavior is ReadOnly and I can update Session Variable

like image 194
Nick Butler Avatar answered Oct 31 '22 20:10

Nick Butler