How to use session storage data in multi layer architecture in C# with ASP.NET MVC?

926 views Asked by At

I am working on a project and using N-Layer architecture (web layer, service layer, data access layer).

I am facing an issue regarding the use of session storage in the data access layer.

I am storing data in the web layer (controller) and I want to use the session stored data in data access layer. Is this possible? If yes then please let me know...

Thanks in advance.

2

There are 2 answers

0
Yehor Androsov On

Shortly: this is possible.

Easy (and really bad) way is to reference web libraries in your data layer and use HttpContext.Current.Session. This will break all the flexibility you got before with your code structure when separating layers.

A bit longer (but much nicer way) is to install some IOC container. It will allow to declare some interfaces in data layer, and register session providers in the presentation layer.

I am going to show the workflow with Ninject. For example, you have some service (SomeService) in the data layer, that needs to operate on data from session. We can use abstractions since SomeService does not really care about origin of the data, it is not that important.

namespace DataLayer
{
    public interface ISomeDataProvider
    {
        string GetData();
    }
}

namespace DataLayer
{
    public class SomeService
    {
        private readonly ISomeDataProvider someDataProvider;

        public SomeService(ISomeDataProvider someDataProvider)
        {
            this.someDataProvider = someDataProvider;
        }

        public void DoThing()
        {
            var data = someDataProvider.GetData();
        }
    }
}

Lets move on to presentation layer. Now we should create implementation for our interface from data layer.

using DataLayer;
using System.Web;

namespace WebProject.App_Start
{
    internal class SessionDataProvider : ISomeDataProvider
    {
        public string GetData()
        {
            return HttpContext.Current.Session["data"].ToString();
        }
    }
}

Finally, we need to configure dependency injection to use our implementation, whenever ISomeDataProvider is used in the constructor. There are plenty of articles about installing Ninject on the web, I recommend Ninject.MVC3 package. Once you install it, you will have NinjectWebCommon.cs look something similar to this.

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(WebProject.App_Start.NinjectWebCommon), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(WebProject.App_Start.NinjectWebCommon), "Stop")]

namespace WebProject.App_Start
{
    using System;
    using System.Web;
    using DataLayer;
    using Microsoft.Web.Infrastructure.DynamicModuleHelper;

    using Ninject;
    using Ninject.Web.Common;

    public static class NinjectWebCommon 
    {
        private static readonly Bootstrapper bootstrapper = new Bootstrapper();

        /// <summary>
        /// Starts the application
        /// </summary>
        public static void Start() 
        {
            DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
            DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
            bootstrapper.Initialize(CreateKernel);
        }
        
        /// <summary>
        /// Stops the application.
        /// </summary>
        public static void Stop()
        {
            bootstrapper.ShutDown();
        }
        
        /// <summary>
        /// Creates the kernel that will manage your application.
        /// </summary>
        /// <returns>The created kernel.</returns>
        private static IKernel CreateKernel()
        {
            var kernel = new StandardKernel();
            try
            {
                kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
                kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

                RegisterServices(kernel);
                return kernel;
            }
            catch
            {
                kernel.Dispose();
                throw;
            }
        }

        /// <summary>
        /// Load your modules or register your services here!
        /// </summary>
        /// <param name="kernel">The kernel.</param>
        private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind<ISomeDataProvider>().To<SessionDataProvider>().InRequestScope();
        }        
    }
}

What is the most important here is this line kernel.Bind<ISomeDataProvider>().To<SessionDataProvider>().InRequestScope();. It will configure SomeService to use SessionDataProvider in data layer without actually referencing all the web dlls in the data layer and bypassing circular dependency

Finally, inject your service in the controller`s constructor

using DataLayer;
using System.Web.Mvc;

namespace WebProject.Controllers
{
    public class HomeController : Controller
    {
        private readonly SomeService someService;

        public HomeController(SomeService someService)
        {
            this.someService = someService;
        }

        public ActionResult Index()
        {
            someService.DoThing();
            return View();
        }
    }
}
0
Bryan On

I believe what you want to do here is implement a custom SessionStateStoreProvider.

https://learn.microsoft.com/en-us/dotnet/api/system.web.sessionstate.sessionstatestoreproviderbase?view=netframework-4.8

Then Session can be implemented as a data layer abstraction and easily shared in that layer.