c# 存储库模式如何从注入的依赖项访问值 class
c# repository pattern how to access values from a dependency injected class
我有一个 c# aspnet mvc(非核心)项目,我在其中使用存储库模式和依赖项注入 (ninject)。
这是一个控制器示例:
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
正在注入并且工作完美,但我想做的是在我的 SecurityRepository
中获取 _appState
class 值而不需要每次都传递给每个方法 userId
和 companyId
。
这是我的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
}
我想做的是拥有这样的东西:
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;
}
}
注入在里面完成 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));
}
我可以遵循任何建议或示例代码吗?
因此,您只需要以某种方式将值从会话传递到您的存储库。其实有很多方法可以达到这个目的。
一种方法是 some-kind-of Initialize
存储库 class 中的方法(当然还有存储库界面):
public class SecurityRepository : ISecurityRepository
{
public void Initialize(AppState appState)
{
_userId = appState.userId;
_sessionId = appState.sessionId;
}
// some other methods
}
然后你可以在你的控制器构造函数中调用它:
public MainController(ISecurityRepository securityRepo)
{
_securityRepo = securityRepo;
_securityRepo.Initialize(Session["appstate"] as AppState);
}
另一种方式 - 您可以在 action filter 中处理设置依赖项。只需添加某种访问器:
public class AppStateAccessor
{
public AppState AppState { get; set; }
}
然后在您的过滤器中设置它:
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;
}
}
然后将此过滤器注册为全局过滤器:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
var kernel = /* get your IKernel somehow */
filters.Add(new RequestScopeDependencySetupFilter(kernel));
}
最后在您的存储库中使用它:
public class SecurityRepository : ISecurityRepository
{
public SecurityRepository(AppStateAccessor accessor)
{
_userId = accessor.AppState.userId;
_sessionId = accessor.AppState.sessionId;
}
// some other methods
}
只是不要忘记正确绑定您的依赖项。此方法的关键点在于您的 AppStateAccessor
实例 是暂时的 并且存在于 请求范围 中。 Ninject(特别是 Ninject.Extensions.ContextPreservation
扩展名)允许您为请求范围绑定 DI 容器中的对象实例:
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...
}
还有一种方法。只需在您的存储库中使用 HttpContext.Current
static 属性:
public class SecurityRepository : ISecurityRepository
{
public SecurityRepository()
{
var appState = HttpContext.Current.Session["appstate"] as AppState;
_userId = appState.userId;
_sessionId = appState.sessionId;
}
// some other methods
}
易于实施,但会向您的存储库添加硬静态依赖项 - 忘掉单元测试吧。
与 类似的另一种方式(这是第 2 种和第 3 种的组合,感谢@Nkosi)。从 AppStateAccessor
:
中提取接口
public interface IAppStateAccessor
{
AppState AppState { get; }
}
然后稍微改变一下AppStateAccessor
:
public class AppStateAccessor
{
public AppState AppState { get; }
= HttpContext.Current.Session["appstate"] as AppState;
}
然后在您的存储库中使用此界面:
public class SecurityRepository : ISecurityRepository
{
public SecurityRepository(IAppStateAccessor accessor)
{
_userId = accessor.AppState.userId;
_sessionId = accessor.AppState.sessionId;
}
// some other methods
}
这次您的服务与 HttpContext
完全解耦,可以轻松 unit-tested.
我有一个 c# aspnet mvc(非核心)项目,我在其中使用存储库模式和依赖项注入 (ninject)。
这是一个控制器示例:
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
正在注入并且工作完美,但我想做的是在我的 SecurityRepository
中获取 _appState
class 值而不需要每次都传递给每个方法 userId
和 companyId
。
这是我的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
}
我想做的是拥有这样的东西:
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;
}
}
注入在里面完成 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));
}
我可以遵循任何建议或示例代码吗?
因此,您只需要以某种方式将值从会话传递到您的存储库。其实有很多方法可以达到这个目的。
一种方法是 some-kind-of Initialize
存储库 class 中的方法(当然还有存储库界面):
public class SecurityRepository : ISecurityRepository
{
public void Initialize(AppState appState)
{
_userId = appState.userId;
_sessionId = appState.sessionId;
}
// some other methods
}
然后你可以在你的控制器构造函数中调用它:
public MainController(ISecurityRepository securityRepo)
{
_securityRepo = securityRepo;
_securityRepo.Initialize(Session["appstate"] as AppState);
}
另一种方式 - 您可以在 action filter 中处理设置依赖项。只需添加某种访问器:
public class AppStateAccessor
{
public AppState AppState { get; set; }
}
然后在您的过滤器中设置它:
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;
}
}
然后将此过滤器注册为全局过滤器:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
var kernel = /* get your IKernel somehow */
filters.Add(new RequestScopeDependencySetupFilter(kernel));
}
最后在您的存储库中使用它:
public class SecurityRepository : ISecurityRepository
{
public SecurityRepository(AppStateAccessor accessor)
{
_userId = accessor.AppState.userId;
_sessionId = accessor.AppState.sessionId;
}
// some other methods
}
只是不要忘记正确绑定您的依赖项。此方法的关键点在于您的 AppStateAccessor
实例 是暂时的 并且存在于 请求范围 中。 Ninject(特别是 Ninject.Extensions.ContextPreservation
扩展名)允许您为请求范围绑定 DI 容器中的对象实例:
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...
}
还有一种方法。只需在您的存储库中使用 HttpContext.Current
static 属性:
public class SecurityRepository : ISecurityRepository
{
public SecurityRepository()
{
var appState = HttpContext.Current.Session["appstate"] as AppState;
_userId = appState.userId;
_sessionId = appState.sessionId;
}
// some other methods
}
易于实施,但会向您的存储库添加硬静态依赖项 - 忘掉单元测试吧。
与 AppStateAccessor
:
public interface IAppStateAccessor
{
AppState AppState { get; }
}
然后稍微改变一下AppStateAccessor
:
public class AppStateAccessor
{
public AppState AppState { get; }
= HttpContext.Current.Session["appstate"] as AppState;
}
然后在您的存储库中使用此界面:
public class SecurityRepository : ISecurityRepository
{
public SecurityRepository(IAppStateAccessor accessor)
{
_userId = accessor.AppState.userId;
_sessionId = accessor.AppState.sessionId;
}
// some other methods
}
这次您的服务与 HttpContext
完全解耦,可以轻松 unit-tested.