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 值而不需要每次都传递给每个方法 userIdcompanyId

这是我的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.