Reputation: 15039
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?
Upvotes: 0
Views: 1499
Reputation: 4002
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.
Upvotes: 1