Autofac - 使用当前类的构造函数参数初始化对象,与构建器所在的类不同

时间:2012-12-14 00:49:49

标签: c# autofac

在下面的代码中,为了清晰起见,使用第二类中的config和monitor是很简单的。如何创建/注册/解决计时器?它似乎无法在第一个类中完成,因为它需要来自第二个类的构造函数参数值,但如果我理解正确,则所有寄存器/解析应该在第一个类中进行。

using System;
using System.ServiceProcess;
using System.Threading;

using Autofac;

namespace MyServiceApp
{
    static class MyServiceAppMain
    {
        static void Main()
        {
            using (var container = InitContainer())
            {
                    container.Resolve<MyService>().Start();
            }
        }

        private static IContainer InitContainer()
        {
            var builder = new ContainerBuilder();
            builder.RegisterType<Configuration>().As<IConfiguration>();
            builder.RegisterType<ServicesMonitor>();
            builder.RegisterType<MyService>();

            IContainer container = 
                builder.Build(Autofac.Builder.ContainerBuildOptions.None);

            return container;
        }
    }

    public partial class MyService : ServiceBase, IMyService
    {
        private Timer _processTimer;
        private int _intervalSize;
        private IConfiguration _config;
        private ServicesMonitor _monitor;

        public MyService(IConfiguration config, ServicesMonitor monitor)
        {
            InitializeComponent();

            _config = config;
            _monitor = monitor;

            _config.ReadAppConfig();
        }

        public void Start()
        {
            OnStart(null);
        }

        protected override void OnStart(string[] args)
        {
            _intervalSize = _config.IntervalMinutes * (60 * 1000);

            _processTimer = 
                new Timer(ProcessTimer_Elapsed, null, Timeout.Infinite, _intervalSize);
        }

        private void ProcessTimer_Elapsed(object sender)
        {
                _processTimer.Change(Timeout.Infinite, _intervalSize);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

我认为你的目标是可测试性或以某种方式抽象出Timer对象的创建。由于Timer实际上没有实现接口,因此您有几个选项。

然而,您正在修改回调中的Timer 这一事实使您的情况变得复杂。从示例中看,您实际上并未进行任何更改 - 您设置了具有特定到期时间和回调期的原始计时器......然后将其设置为相同的内容。

选项一:做你正在做的事

控制反转很有意义,但是你不能真正“伪造”计时器或“交换”计时器以用于其他一些实现。如上所述,它没有实现任何ITimer接口或类似的东西。

如果您不必将其换掉......只是让它发生。在测试期间交换配置值和内容,使其更适合您的测试并称之为好。

选项二:包裹计时器

如果你需要在课外创建它,你需要把它包装成......。然后你可以换掉那个包装器。

由于您正在修改Timer本身,因此您需要包含一些该功能。

public class SelfAdjustingTimer
{
  public Timer Timer { get; protected set; }
  public int Interval { get; private set; }
  public SelfAdjustingTimer(int interval)
  {
    this.Interval = interval;
    this.Timer = new Timer(Callback, null, Timeout.Infinite, interval);
  }
  private void Callback(object sender)
  {
    this.Timer.Change(Timeout.Infinite, this.Interval);
  }
}

如果你需要知道它何时发生或根据它做一些工作,你甚至可以在回调期间添加一个事件或引发的事件。

选项三:Crazy Lambda Handler

它可能变得非常复杂,但是您知道如何使用lambda表达式创建事件处理程序,例如......

someObj.TheEvent += (sender, args) => { /* Do Work */ };

您假设可以找到一种方法来使用lambda表达式构建动态自引用回调并以此方式创建计时器回调。我以前做过这样的事情......它有点令人困惑。这就像生成lambdas的lambda一样,需要一段时间来弄明白(这就是为什么我不在这里交给你)。

如果您不必修改回叫中的计时器

...那么你有更多的选择。你可以......

  • 创建ITimerFactory并根据配置为您生成计时器。将其注入您的班级并使用它。 (ITimerFactory及其实现是你必须创建的东西。它们不存在。)
  • 使用TimerBaseTimerWrapper在ASP.NET中创建HttpContextBaseHttpContextWrapper。将TimerBase注入您的课程,而不是新建Timer。对于TimerBase的DI注册,您可以在此处查找配置值并设置真实的Timer

......等等。所有选项都涉及抽象出具体Timer对象的概念,因为它再次没有实现任何接口,也不是你可以从中得到的东西。

如果是我 ...我可能只是做你正在做的事情,尽可能地将代码隔离到一个较小的服务类中,该服务类与{{1并且具有可模拟的更好的方法(例如,用某些东西包裹Timer)然后使用包装器尽可能多地进行测试。