好吧,您已经迈出了良好的第一步,认识到 Web.config 只是另一个依赖项并将其包装到 ConfigProvider 中进行注入是一个很好的解决方案。
但是,您遇到了 MVC 的设计问题之一 - 即,要对 DI 友好,属性应该只提供元数据,但 never actually define behavior。这不是您的测试方法的问题,而是过滤器设计方法的问题。
正如帖子中所指出的,您可以通过将操作过滤器属性分成两部分来解决此问题。
- 一个不包含任何行为来标记您的控制器和操作方法的属性。
- 实现IActionFilter 并包含所需行为的DI 友好类。
方法是使用 IActionFilter 来测试属性是否存在,然后执行所需的行为。动作过滤器可以提供所有依赖项,然后在组合应用程序时注入。
IConfigProvider provider = new WebConfigProvider();
IActionFilter filter = new MaxLengthActionFilter(provider);
GlobalFilters.Filters.Add(filter);
注意:如果您需要过滤器的任何依赖项的生命周期短于单例,您将需要使用GlobalFilterProvider,如this answer。
MaxLengthActionFilter 的实现如下所示:
public class MaxLengthActionFilter : IActionFilter
{
public readonly IConfigProvider configProvider;
public MaxLengthActionFilter(IConfigProvider configProvider)
{
if (configProvider == null)
throw new ArgumentNullException("configProvider");
this.configProvider = configProvider;
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
var attribute = this.GetMaxLengthAttribute(filterContext.ActionDescriptor);
if (attribute != null)
{
var maxLength = attribute.MaxLength;
// Execute your behavior here, and use the configProvider as needed
}
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var attribute = this.GetMaxLengthAttribute(filterContext.ActionDescriptor);
if (attribute != null)
{
var maxLength = attribute.MaxLength;
// Execute your behavior here, and use the configProvider as needed
}
}
public MaxLengthAttribute GetMaxLengthAttribute(ActionDescriptor actionDescriptor)
{
MaxLengthAttribute result = null;
// Check if the attribute exists on the controller
result = (MaxLengthAttribute)actionDescriptor
.ControllerDescriptor
.GetCustomAttributes(typeof(MaxLengthAttribute), false)
.SingleOrDefault();
if (result != null)
{
return result;
}
// NOTE: You might need some additional logic to determine
// which attribute applies (or both apply)
// Check if the attribute exists on the action method
result = (MaxLengthAttribute)actionDescriptor
.GetCustomAttributes(typeof(MaxLengthAttribute), false)
.SingleOrDefault();
return result;
}
}
而且,您的属性不应包含任何行为应该如下所示:
// This attribute should contain no behavior. No behavior, nothing needs to be injected.
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
public class MaxLengthAttribute : Attribute
{
public MaxLengthAttribute(int maxLength)
{
this.MaxLength = maxLength;
}
public int MaxLength { get; private set; }
}
采用更松散耦合的设计,测试属性是否存在更加直接。
[TestMethod]
public void Base_controller_must_have_MaxLengthFilter_attribute()
{
var att = typeof(BaseController).GetCustomAttribute<MaxLengthAttribute>();
Assert.IsNotNull(att);
}