用静态方法代替规范

时间:2017-02-11 06:56:20

标签: c# .net unit-testing mspec fody

我试图使用Ionad plugin for Fody。我创建了我的替换,但是依赖代码没有调用替换。

替代

[StaticReplacement(typeof(AppUtils))]
public static class AppUtilsSubsitute
{
    public static void LogException(string func, Exception ex) => Write("Exception", ex.ToJson());

    public static void LogError(string err) => Write("Error", err);

    public static void LogInfo(string info) => Write("Info", info);

    public static void LogWarning(string info) => Write("Warning", info);

    public static void LogCypherQuery(string func, ICypherFluentQuery query) => Write(func, query.ToJson());

    static void Write(string logType, string message)
    {
        var dictionary = new Dictionary<string, string> { { logType, message } };
        var assembly = Assembly.GetExecutingAssembly();
        using (var writer = new StreamWriter(assembly.Location + "Machine.Specifications.log"))
        {
            writer.Write(dictionary.ToJson());
        }
    }

    public static Dictionary<string, string> Log()
    {
        var assembly = Assembly.GetExecutingAssembly();
        Dictionary<string, string> content;
        using (var reader = new StreamReader(assembly.GetManifestResourceStream("Machine.Specifications.log")))
        {
            content = JsonConvert.DeserializeObject<Dictionary<string, string>>(reader.ReadToEnd());
        }
        return content;
    }
}

依赖方法

[AttributeUsage(AttributeTargets.Method)]
public class EnsurePresencesOfAttribute : ActionFilterAttribute
{
    internal virtual string Required { get; }
    internal virtual string Param { get; }
    public EnsurePresencesOfAttribute(string required = "", string param="request")
    {
        Required = required;
        Param = param;
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        Dictionary<string, object> model = actionContext.ActionArguments;
        if (model == null || model.Count == 0 || !model.ContainsKey(Param))
        {
            ValueError(actionContext, $"{Param} parameter");
            return;
        }
        foreach (var requirement in Required.Split(',').Select(e => e.Trim()))
        {
            if (model[requirement] == null)
            {
                ValueError(actionContext, requirement);
                return;
            }
        }
        base.OnActionExecuting(actionContext);
    }

    public override Task OnActionExecutingAsync(HttpActionContext context, CancellationToken token)
    {
        Dictionary<string, object> model = context.ActionArguments;
        if(model == null || model.Count == 0 || !model.ContainsKey(Param))
        {
            ValueError(context, $"{Param} parameter");
            return Task.FromResult(0);
        }
        foreach (var requirement in Required.Split(',').Select(e => e.Trim()))
        {
            if (model[requirement] == null)
            {
                ValueError(context, requirement);
                Task.FromResult(0);
            }
        }
        return base.OnActionExecutingAsync(context, token);
    }

    private static void ValueError(HttpActionContext context, string requirement)
    {
        var action = context.ActionDescriptor.ActionName;
        AppUtils.LogError($"{action} Failed : Missing Required Attribute {requirement}. ");
        using (var controller = new BaseApiController { Request = new HttpRequestMessage() })
        {
            controller.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, new HttpConfiguration());
            context.Response = controller.InvalidInputResponse();
        }
    }
}

1 个答案:

答案 0 :(得分:2)

静态依赖性往往使维护和测试与它们紧密耦合的代码变得困难。

ASP.NET Web API提供了可扩展性点,以避免出现这种依赖关系。主要是用于Dependency Injection in ASP.NET Web API 2IDependencyResolver

有了这个,我建议将静态实用程序转换为抽象,或者至少将其抽象为可以轻松替换的接口。

public interface ILogger {
    void LogException(string func, Exception ex);
    void LogError(string err);
    void LogInfo(string info);
    void LogWarning(string info);
    void LogCypherQuery(string func, ICypherFluentQuery query);
    Dictionary<string, string> Log();
}

这将允许任意数量的实现,尤其是可以在需要时用于单元测试的存根。

public class AppUtilsSubsitute : ILogger {
    public void LogException(string func, Exception ex) => Write("Exception", ex.ToJson());

    public void LogError(string err) => Write("Error", err);

    public void LogInfo(string info) => Write("Info", info);

    public void LogWarning(string info) => Write("Warning", info);

    public void LogCypherQuery(string func, ICypherFluentQuery query) => Write(func, query.ToJson());

    static void Write(string logType, string message) {
        var dictionary = new Dictionary<string, string> { { logType, message } };
        var assembly = Assembly.GetExecutingAssembly();
        using (var writer = new StreamWriter(assembly.Location + "Machine.Specifications.log")) {
            writer.Write(dictionary.ToJson());
        }
    }

    public Dictionary<string, string> Log() {
        var assembly = Assembly.GetExecutingAssembly();
        Dictionary<string, string> content;
        using (var reader = new StreamReader(assembly.GetManifestResourceStream("Machine.Specifications.log"))) {
            content = JsonConvert.DeserializeObject<Dictionary<string, string>>(reader.ReadToEnd());
        }
        return content;
    }
}

依赖代码现在将被重构为依赖于抽象而不是可能是任何东西的实施。

private static void ValueError(HttpActionContext context, string requirement) {
    var httpConfiguration = context.RequestContext.Configuration;
    var resolver = httpConfiguration.DependencyResolver;
    var logger = resolver.GetService(typeof(ILogger));
    var action = context.ActionDescriptor.ActionName;
    logger.LogError($"{action} Failed : Missing Required Attribute {requirement}. ");
    using (var controller = new BaseApiController { Request = new HttpRequestMessage() }) {
        //USE THE GLOBAL HTTPCONFIGURATION ALREADY ASSOCIATED WITH THE
        //REQUEST INSTEAD OF CREATING A NEW ONE THAT HAS NONE OF THE 
        //WEB API CONFIG THAT WOULD HAVE BEEN ADDED AT STARTUP
        controller.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, httpConfiguration);
        context.Response = controller.InvalidInputResponse();
    }
}

确保在启动时使用您选择的IoC容器配置Dependency Resolver。

  

DependencyResolver属性上设置依赖项解析器   全局HttpConfiguration对象。

     

以下代码向Unity注册ILogger接口   然后创建一个UnityResolver

public static void Register(HttpConfiguration config)    {
    var container = new UnityContainer();
    container.RegisterType<ILogger, AppUtilsSubsitute>(new HierarchicalLifetimeManager());
    config.DependencyResolver = new UnityResolver(container);

    // Other Web API configuration not shown.    
}