【问题标题】:Substituting Static Methods for Spec用静态方法代替规范
【发布时间】:2017-02-11 06:56:20
【问题描述】:

我正在尝试使用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();
        }
    }
}

【问题讨论】:

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


    【解决方案1】:

    静态依赖往往会使与它们紧密耦合的代码难以维护和测试。

    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 对象。

    以下代码将ILogger 接口注册到 Unity 和 然后创建一个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.    
    }
    

    【讨论】:

      猜你喜欢
      • 2022-07-22
      • 1970-01-01
      • 2012-10-11
      • 1970-01-01
      • 1970-01-01
      • 2011-02-12
      • 1970-01-01
      • 2020-10-18
      • 1970-01-01
      相关资源
      最近更新 更多