0

I have a c# aspnet mvc (not core) project where Im using repository pattern and dependency injection (ninject).

This is a sample of a controller:

public class MainController : Controller
{
    ISecurityRepository _securityRepo = default(ISecurityRepository);
    AppState _appState;

    public MainController(ISecurityRepository securityRepo)
    {
        _securityRepo = securityRepo;
        _appState = (AppState)Session["appstate"];
    }

    public ActionResult Employee(int employeeId)
    {
        var permissions = _securityRepo.GetEmployeePermissions(
            employeeId,
            _appState.userId,
            _appState.sessionId);

        return View(permissions);
    }

    public ActionResult Leave(int leaveId, int employeeId)
    {
        var permissions = _securityRepo.GetEmployeeLeavePermissions(
                leaveId,
                employeeId,
                _appState.userId,
                _appState.sessionId);

        return View(permissions);
    }

    public ActionResult Holiday(int holidayId, int employeeId)
    {
        var permissions = _securityRepo.GetEmployeeHolidayPermissions(
            holidayId,
            employeeId,
            _appState.userId,
            _appState.sessionId);

        return View(permissions);
    }
}

ISecurityRepository is being injected and thats working perfect, but what I want to do is like to get in my SecurityRepository the _appState class values without the need to pass into every method userId and companyId every time.

This is my SecurityRepository.cs

public class SecurityRepository : ISecurityRepository
{
    public EmployeePermissions GetEmployeePermissions(int employeeId, int userId, int sessionId)
    {
        // ... some code here
        return employeePermissions;
    }

    public EmployeeLeavePermissions GetEmployeeLeavePermissions(int leaveId, int employeeId, int userId, int sessionId)
    {
        // ... some code here
        return employeeLeavePermissions;
    }

    public EmployeeHolidayPermissions GetEmployeeHolidayPermissions(int holidayId, int employeeId, int userId, int sessionId)
    {
        // ... some code here
        return employeeHolidayPermissions;
    }

    // ... some more methods

}

What I would like to do is to have something like this:

public class SecurityRepository : ISecurityRepository
{
    // here have maybe a constructor that receives the AppState variable from MainController, or a public property

    public EmployeePermissions GetEmployeePermissions(int employeeId)
    {
        // a way that I can user _appState here!!
        return employeePermissions;
    }
}

The injection is done inside NinjectWebCommon class

/// <summary>
        /// Load your modules or register your services here!
        /// </summary>
        /// <param name="kernel">The kernel.</param>
        private static void RegisterServices(IKernel kernel)
        {
            kernel.Bind(typeof(ISecurityRepository)).To(typeof(Data.SecurityRepository));


        }

Any advice or sample code that I can follow?

VAAA
  • 14,531
  • 28
  • 130
  • 253
  • how do you register your repository within a ninject? are you on `asp.net` or on `asp.net core`? – vasily.sib Jul 05 '19 at 03:32
  • asp.net (not core) and inside NinjectWebCommon.cs I have in the RegisterService method: kernel.Bind(typeof(ISecurityRepository)).To(typeof(Data.SecurityRepository)); – VAAA Jul 05 '19 at 03:34
  • Please edit you question and paste you code there. So, you just need to somehow pass value from session to you repository? – vasily.sib Jul 05 '19 at 03:43
  • I need to pass not only session, maybe other information but that could live in AppState object. Let me edit and show how the injection is done in NinjectWebCommon. – VAAA Jul 05 '19 at 03:45
  • Create a factory that will receive your _appState and return the repositiry initialized with _appState. Then inject factory instead repositiry. – Artur Jul 05 '19 at 04:43
  • Do you have a sample of how to achieve this? – VAAA Jul 05 '19 at 09:37

1 Answers1

1

So, you just need to somehow pass value from session to you repository. Actually there is a lot of ways to achive this.

One way would be a some-kind-of Initialize method in your repository class (and in your repository interface, ofcourse):

public class SecurityRepository : ISecurityRepository
{
    public void Initialize(AppState appState)
    {
        _userId = appState.userId;
        _sessionId = appState.sessionId;
    }

    // some other methods
}

Then you may call it in your controller constructor:

public MainController(ISecurityRepository securityRepo)
{
    _securityRepo = securityRepo;
    _securityRepo.Initialize(Session["appstate"] as AppState);
}

Another way - you can handle setting your dependency in the action filter. Just add some kind of accessor:

public class AppStateAccessor
{
    public AppState AppState { get; set; }
}

Then set it in your filter:

public class RequestScopeDependencySetupFilter : ActionFilterAttribute
{
    private readonly IKernel _kernel;

    public RequestScopeDependencySetupFilter(IKernel kernel)
    {
        _kernel = kernel;
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        var accessor = _kernel.Get<AppStateAccessor>();
        accessor.AppState = context.HttpContext.Session["appstate"] as AppState;
    }
}

Then register this filter as global:

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    var kernel = /* get your IKernel somehow */
    filters.Add(new RequestScopeDependencySetupFilter(kernel));
}

And finally use it in your repository:

public class SecurityRepository : ISecurityRepository
{
    public SecurityRepository(AppStateAccessor accessor)
    {
        _userId = accessor.AppState.userId;
        _sessionId = accessor.AppState.sessionId;
    }

    // some other methods
}

Just don't forget to bind your dependencies right. The key point of this method is that your AppStateAccessor instance is transient and live in request scope. Ninject (specificly Ninject.Extensions.ContextPreservation extension) allows you to bind instance of object in DI container for a request scope:

private static void RegisterServices(IKernel kernel)
{
    // bind AppStateAccessor in request scope
    kernel.Bind<AppStateAccessor>().ToSelf().InRequestScope();

    // transient repository
    kernel.Bind<ISecurityRepository>().To<Data.SecurityRepository>();

    // other dependencies...

}

And one more way. Just use HttpContext.Current static property in your repository:

public class SecurityRepository : ISecurityRepository
{
    public SecurityRepository()
    {
        var appState = HttpContext.Current.Session["appstate"] as AppState;

        _userId = appState.userId;
        _sessionId = appState.sessionId;
    }

    // some other methods
}

Easy to implement, but add a hard static dependency to your repository - forget about unit testing.


Just one more way (which is a combination of 2nd and 3rd, thanks @Nkosi) simular to this answer. Extract interface from AppStateAccessor:

public interface IAppStateAccessor
{
    AppState AppState { get; }
}

Then change AppStateAccessor a bit:

public class AppStateAccessor
{
    public AppState AppState { get; }
        = HttpContext.Current.Session["appstate"] as AppState;
}

Then use this interface in your repository:

public class SecurityRepository : ISecurityRepository
{
    public SecurityRepository(IAppStateAccessor accessor)
    {
        _userId = accessor.AppState.userId;
        _sessionId = accessor.AppState.sessionId;
    }

    // some other methods
}

This time your service is fully decoupled from HttpContext and can be easily unit-tested.

vasily.sib
  • 3,871
  • 2
  • 23
  • 26
  • @vasily.sib Go with the same accessor design but instead of having to use a filter, have the accessor implementation wrap the static context. Simplifies the design and still decouples the context. – Nkosi Jul 05 '19 at 10:07
  • @Nkosi how is that done? Thanks a lot also @vasily.sib? – VAAA Jul 05 '19 at 12:32
  • @VAAA To avoid taking away from the already provided answer, check a similar answer I gave here. https://stackoverflow.com/a/45908900/5233410 Instead of the user principal you will be accessing the session. – Nkosi Jul 05 '19 at 12:52
  • @Nkosi Is that code could work on aspnet Mvc that is not dotnetcore? Thanks a lot – VAAA Jul 05 '19 at 13:40
  • @VAAA that code is not core and it can work with MVC – Nkosi Jul 05 '19 at 13:42
  • @vasily.sib yes you did. – Nkosi Jul 07 '19 at 10:52