在构造函数中访问假DI的HttpContext

时间:2018-06-02 09:29:48

标签: c# asp.net-mvc dependency-injection httpcontext

我正在开发一个没有DI或单元测试的asp.net mvc应用程序。所以我开始通过将应用程序拆分为3层来重组应用程序以进行单元测试:控制器 - 服务 - DataAccess。

一些控制器使用Session和Cookie来存储和检索值。所以我创建了一个接口和一个类来处理从Session和Cookies中保存和检索值。

我这样做只是通过使用单元测试而从不运行应用程序。

由于应用程序没有DI我在控制器的构造函数上创建了ContextService,它通过给控制器的HttpContext作为输入参数。

但是,当我运行应用程序时,未在会话或Cookie中检索或保存这些值。似乎HttpContext在contructor上为null。

问题1: 我应该如何处理我的ContextService。它应该使用静态属性HttpContext.Current来访问会话和cookie(它将如何进行单元测试)或......?

问题2: 如果你知道另一个解决方案,那么为了将来还有DI,它应该如何适应。

1 个答案:

答案 0 :(得分:1)

  

我在控制器的构造函数上创建了ContextService,它给出了Controller的HttpContext作为输入参数。

通过将HttpContext从控制器传递给服务,您可以让控制器负责创建该服务。这将控制器与服务紧密耦合,而松散耦合是目标。

  

它应该使用静态属性HttpContext.Current来访问会话和cookie

     

如何进行单元测试

它没有赢。这是我们创建抽象的重要原因。我们系统中的某些部件无法进行单元测试,我们希望能够将它们替换为我们在测试中使用的假实现。

然而,诀窍是让被替换的部分尽可能小,并且最好不要将它与业务逻辑混合,因为替换它也意味着你不会测试那个逻辑。 / p>

您应该隐藏对抽象背后的HttpContext.Current的访问权限。但是,当您这样做时,请确保以最适合您的应用程序的方式定义抽象。例如,仔细查看ContextService想要的内容。它真的想要访问cookie吗?可能不是。或者它是否想要当前登录用户的名称或ID?这更有可能。所以你应该围绕它建模你的抽象。

作为示例,定义一个抽象,允许应用程序代码使用IUserContext访问有关登录用户的信息:

public interface IUserContext
{
    string UserName { get; }
}

此抽象的一种可能实现是从HTTP cookie检索此信息的实现:

public class CookieUserContext : IUserContext
{
    public string UserName => HttpContext.Current.Cookies["name"];
}

但您可以轻松想象其他实现,例如,当相同的应用程序代码需要在Web请求的上下文之外运行时,例如作为后台操作的一部分,或者是隔离的Windows服务应用程序。这是引入抽象的另一个重要原因 - 只要相同的代码需要能够在不同的环境中运行。

如果您有兴趣,Mark Seemann撰写的这本书Dependency Injection in .NET会详细介绍这些模式和原则,例如应用DI的原因,防止紧密耦合。 Seemann和我自己的second edition of this book(预计将于2018年秋季发布)甚至会详细介绍您正在努力解决的问题,例如防止漏洞抽象,如何将行为分成类,以及设计应用程序使用SOLID原则。该书的主页包含第一章的下载链接,可以免费下载。