在假 DI 的构造函数中访问 HttpContext
Access HttpContext in constructor for fake DI
我正在开发 asp.net mvc 应用程序,它还没有 DI 或单元测试。因此,我开始通过将应用程序分为 3 层来重组应用程序以进行单元测试:控制器 - 服务 - 数据访问。
一些控制器使用 Session 和 Cookie 来存储和检索值。所以我创建了一个接口和一个 class 来处理从 Session 和 Cookies 中保存和检索值。
我只是通过使用单元测试而不是 运行 应用程序来做到这一点。
由于应用程序没有 DI,我通过将控制器的 HttpContext 作为输入参数在控制器的构造函数上创建了 ContextService。
然而,当我 运行 应用程序时,这些值没有被检索或保存在 Session 或 Cookies 中。构造函数上的 HttpContext 似乎为空。
问题一:
我应该如何处理我的 ContextService。它应该使用静态 属性 HttpContext.Current 来访问会话和 cookie(将如何进行单元测试)还是...?
问题二:
如果你知道另一种解决方案应该如何调整以便将来也有 DI。
I created on the contructor of the controller the ContextService by giving as an input parameter the HttpContext of the Controller.
通过将 HttpContext
从控制器传递给服务,您让控制器负责创建该服务。这将控制器与服务紧密耦合,而目标是松散耦合。
hould it use the static property HttpContext.Current in order to access the session and cookies
how will it be unit tested
不会。这是我们创建抽象的一个重要原因。我们系统中的某些部分无法进行单元测试,我们希望能够用我们在测试中使用的虚假实现替换它们。
然而,诀窍是让替换的部分尽可能小,并且最好不要将其与业务逻辑混合,因为替换它也意味着您不会测试该逻辑。
您应该将对 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 服务申请。这是引入抽象的另一个重要原因——只要相同的代码需要能够 运行 在不同的环境中。
如果您感兴趣,Seemann 和我自己的书 Dependency Injection in .NET, by Mark Seemann, goes into great detail about these kinds of patterns and principles, such as reasons for applying DI, preventing tight coupling. The second edition of this book 甚至会更详细地介绍您正在努力解决的问题,例如防止泄漏抽象、如何将行为分离到 类,并使用 SOLID 原则设计应用程序。本书首页有第一章下载link,可免费下载
我正在开发 asp.net mvc 应用程序,它还没有 DI 或单元测试。因此,我开始通过将应用程序分为 3 层来重组应用程序以进行单元测试:控制器 - 服务 - 数据访问。
一些控制器使用 Session 和 Cookie 来存储和检索值。所以我创建了一个接口和一个 class 来处理从 Session 和 Cookies 中保存和检索值。
我只是通过使用单元测试而不是 运行 应用程序来做到这一点。
由于应用程序没有 DI,我通过将控制器的 HttpContext 作为输入参数在控制器的构造函数上创建了 ContextService。
然而,当我 运行 应用程序时,这些值没有被检索或保存在 Session 或 Cookies 中。构造函数上的 HttpContext 似乎为空。
问题一: 我应该如何处理我的 ContextService。它应该使用静态 属性 HttpContext.Current 来访问会话和 cookie(将如何进行单元测试)还是...?
问题二: 如果你知道另一种解决方案应该如何调整以便将来也有 DI。
I created on the contructor of the controller the ContextService by giving as an input parameter the HttpContext of the Controller.
通过将 HttpContext
从控制器传递给服务,您让控制器负责创建该服务。这将控制器与服务紧密耦合,而目标是松散耦合。
hould it use the static property HttpContext.Current in order to access the session and cookies
how will it be unit tested
不会。这是我们创建抽象的一个重要原因。我们系统中的某些部分无法进行单元测试,我们希望能够用我们在测试中使用的虚假实现替换它们。
然而,诀窍是让替换的部分尽可能小,并且最好不要将其与业务逻辑混合,因为替换它也意味着您不会测试该逻辑。
您应该将对 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 服务申请。这是引入抽象的另一个重要原因——只要相同的代码需要能够 运行 在不同的环境中。
如果您感兴趣,Seemann 和我自己的书 Dependency Injection in .NET, by Mark Seemann, goes into great detail about these kinds of patterns and principles, such as reasons for applying DI, preventing tight coupling. The second edition of this book 甚至会更详细地介绍您正在努力解决的问题,例如防止泄漏抽象、如何将行为分离到 类,并使用 SOLID 原则设计应用程序。本书首页有第一章下载link,可免费下载