在控制器 另一个抽象接口(IConfiguration)与IRepository沿(相貌丑陋的我) - 而且它不会工作 因为配置类是静态的并且不能实现接口
大胆的声明并不完全正确。例如:
public interface IManageConfigurations
{
string DefaultFileLocation { get; }
}
public class ConfigurationManagerImpl : IManageConfigurations
{
public string DefaultFileLocation
{
get { return ConfigurationManager.AppSettings["DefaultFileLocation"]; }
}
}
public class MyController : Controller
{
private readonly IRepository _repository;
private readonly IManageConfigurations _config;
public MyController(IRepository repository, IManageConfigurations config)
{
_repository = repository;
_config = config;
}
}
我不认为这是一个丑陋的解决方案,但这主要是一种美学观点,这是主观的。在大多数情况下,我不认为一个类要揭示超注射反模式,直到它有大约5个或更多的接口依赖。此外,这几乎是单元可测试代码中依赖性解析的标准解决方案。您可以模拟出配置界面,就像您嘲笑仓库一样。以上是我如何使用web.config中保存的值解决依赖关系。
另一种解决方案是在控制器中对ConfigurationManager.AppSettings["key"]
调用进行硬编码。然后,在您的单元测试项目中,您可以在app.config文件中设置相同的值。然后,单元测试运行器将查找app.config中的值,而Web服务器将在web.config中查找它们。
public class MyController : Controller
{
public ActionResult Index()
{
// the below will get the value from the unit test project's app.config
// when run as a unit test, but will get the value from web.config in server
var fileLocation = ConfigurationManager.AppSettings["DefaultFileLocation"];
}
}
更新
如果你真的想保持字符串静态类,并有单元测试代码,你可以这样做:
public static class ConfigSettings
{
public static string DefaultFileLocation
{
get { return ConfigurationManager.AppSettings["DefaultFileLocation"]; }
}
}
然后,您可以使用静态从Controller
内免费上课。但是,为了提供作为单元测试运行时的值,如上所述,您必须在单元测试项目的app.config
文件中放置一个appSettings
节点,这非常类似于您的web项目的web.config
文件中的appSettings
节点。单元测试运行器将从app.config中提取值,Web服务器将从web.config中提取值。
UnitTestProject \ App.config中
<configuration>
<appSettings>
<!-- use this value when executed in unit test runner -->
<add key="DefaultFileLocation"
value="C:\Users\me\test_files\test_file.png" />
</appSettings>
</configuration>
MvcProject \ web.config文件(默认)
<configuration>
<appSettings>
<!-- use this value when executed locally in IIS Express -->
<add key="DefaultFileLocation"
value="C:\Users\me\Documents\Visual Studio 20xx\Projects\MyProj\App_Data\default_files\default_logo.png" />
</appSettings>
</configuration>
MvcProject \ web.Release.config(变换)
<configuration>
<appSettings>
<!-- use this value when executed on live IIS production server -->
<add key="DefaultFileLocation"
value="E:\approot\siteroot\App_Data\default_files\default_logo.png"
xdt:Transform="SetAttributes" xdt:Locator="Match(key)" />
</appSettings>
</configuration>
如果这没有回答你的问题,也许我还没有完全理解你以后的事情。
更新
我唯一缺少的解决方案中的一个部分 - 如何使用从控制器内的公共 静态类ConfigSettings?我应该将 这个静态类作为参数传递给构造函数吗?
不,你不需要做任何特别的事情。只要使用它作为一个类,就像你会使用静态ConfigurationManager
。
public ActionResult MyAction()
{
var customFilePath = GetCustomFilePath(); // may not be set
if (string.IsNullOrWhiteSpace(customFilePath)) // fall back to default
customFilePath = ConfigSettings.DefaultFilePath;
}
所以你的控制器行动正在对静态ConfigSettings
类的依赖,但它不是在这种情况下,注射/热插拔依赖。当单元测试运行器调用此方法并且customFilePath为null/empty/whitespace时,它将进入ConfigSettings get方法,该方法将调用ConfigurationManager.AppSettings,该方法将在单元测试项目的app.config/appSettings中查找该值。
如果将配置常量值作为XML文件存储,或者作为解决方案的一部分或在外部存储(但仍然可以在程序中仍可访问的位置),该怎么办?这样,您可以在XML文件中编辑配置值,而无需更改代码(避免对值进行硬编码)。 – 2013-03-24 12:52:05
感谢您的回答。 XML是一个好主意,但是你知道,Web.Config也是XML。它不是使用什么格式,而是如何实现常量,使它们在代码外部,但仍然保持控制器可测试性(因为它们是外部的,它们引入依赖性) - 我正在为这个确切的困境而挣扎。 – 2013-03-24 12:55:21