首页 > 解决方案 > c#存储库模式如何访问依赖注入类中的值

问题描述

我有 ac#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无需每次都传递给每个方法。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课堂内完成

/// <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));


        }

我可以遵循的任何建议或示例代码?

标签: c#dependency-injectioninversion-of-controlninject

解决方案


因此,您只需要以某种方式将值从会话传递到您的存储库。实际上有很多方法可以实现这一点。

一种方法是在您的存储库类中使用某种Initialize方法(当然,在您的存储库接口中):

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);
}

另一种方式 - 您可以在操作过滤器中设置您的依赖项。只需添加某种访问器:

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在存储库中使用静态属性:

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并且可以很容易地进行单元测试。


推荐阅读