那麼,你已經採取了良好的第一步,認識到Web.config只是另一個依賴項,並將其包裝到ConfigProvider中以便注入是一個很好的解決方案。
但是,你正在被MVC的一個設計問題絆倒 - 即爲了DI友好,屬性應該只提供元數據,但是never actually define behavior。這對您的測試方法不是問題,這是過濾器設計方法的一個問題。
正如在帖子中指出的那樣,您可以通過將動作過濾器屬性分成兩部分來解決此問題。
- 一個不包含標記控制器和操作方法行爲的屬性。
- 一個DI友好的類,實現IActionFilter幷包含所需的行爲。
該方法是使用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);
}
謝謝@ NightOwl888!這確實是我可以沉入其中的一個答案,並且易於理解。我一直認爲屬性不應該定義行爲,但是我內省的有限時間迫使我爲動作過濾器屬性設置一個例外。 – ProfK 2014-12-14 16:59:55