在前面使用Topshelf的文章里,我们的工作类TownCrier使用的是无参数的构造函数,满足测试的目的。在实际的开发过程中,我们常常需要使用带有参数的构造函数,就不可避免的使用Ioc的技术。在这里我们使用的是Topshelf.Autofac这个开源框架。
1.安装Topshelf.Autofac
install-package Topshelf.Autofac -Version 3.1.0
2.创建带有参数的TownCrier构造函数类
public class TownCrier { readonly Timer _timer; private AM am; public TownCrier(AM parameter) { am = parameter; _timer = new Timer(1000) { AutoReset = true }; _timer.Elapsed += (sender, eventArgs) => Console.WriteLine("It is {0} and all is well", DateTime.Now); } public void Start() { _timer.Start(); } public void Stop() { _timer.Stop(); } } public class AM { public string Name { get; set; } }
这里纯粹是为了进行测试,AM类没有任何其他方法,但可以达到测试的目的。
3.配置Ioc
public static void TestIoc() { // Autofac var builder = new ContainerBuilder(); builder.RegisterType<TownCrier>(); builder.RegisterType<AM>(); var container = builder.Build(); var rc = HostFactory.Run(x => //1 { x.Service<TownCrier>(s => //2 { s.ConstructUsing(() => container.Resolve<TownCrier>()); //3 s.WhenStarted(tc => tc.Start()); //4 s.WhenStopped(tc => tc.Stop()); //5 }); x.RunAsLocalSystem(); //6 x.SetDescription("Sample Topshelf Host"); //7 x.SetDisplayName("Stuff"); //8 x.SetServiceName("Stuff"); //9 }); //10 var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode()); //11 Environment.ExitCode = exitCode; }
代码中的红色版本是配置的主要地方,简单2点,一是将涉及到的类,通过RegisterType方法注册进来。二是ConstructUsing函数使用函数表达式container.Resolve方法注册我们的工作类TownCrier。
在测试这段代码的时候,遇见的问题是根据官方提供的demo案例是不对的,根本没有对应的方法,可能是不同版本或者github网站代码和nuget安装代码不是同一份的缘故,反正这里是一个坑。以下是官方Demo:
static void Main(string[] args) { // Create your container var builder = new ContainerBuilder(); builder.RegisterType<SampleDependency>().As<ISampleDependency>(); builder.RegisterType<SampleService>(); var container = builder.Build(); HostFactory.Run(c => { // Pass it to Topshelf c.UseAutofacContainer(container); c.Service<SampleService>(s => { // Let Topshelf use it s.ConstructUsingAutofacContainer(); s.WhenStarted((service, control) => service.Start()); s.WhenStopped((service, control) => service.Stop()); }); }); }
以上代码测试通过之后,就对Topshelf.Autofac的依赖注入有了基本了解,就可以着手自己实际业务的Ioc处理了。
实际项目中的应用代码案例:
public static void RunService() { // IOC Autofac var builder = new ContainerBuilder(); builder.RegisterAssemblyTypes(Assembly.Load("HengShen.Pts.Domain")).Where(i => i.Namespace == "HengShen.Pts.Domain.Services"); builder.RegisterAssemblyTypes(Assembly.Load("HengShen.Pts.Domain")).Where(i => i.Namespace == "HengShen.Pts.Domain.TopshelfSevice"); builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)); builder.RegisterType<SqlUowDbContext>().As<ISqlUnitOfWork>(); var container = builder.Build(); var rc = HostFactory.Run(x => //1 { try { x.Service<WavePlanSyncService>(s => //2 { s.ConstructUsing(() => container.Resolve<WavePlanSyncService>()); s.WhenStarted(tc => tc.Start()); s.WhenStopped(tc => tc.Stop()); }); x.RunAsLocalSystem(); //6 x.SetDescription("Sample Topshelf Host"); //7 x.SetDisplayName("Stuff"); //8 x.SetServiceName("Stuff"); //9 } catch(Exception ex) { string m = ex.Message; } }); //10 var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode()); //11 Environment.ExitCode = exitCode; }
考虑到以后还会有新的task加入,这里就不在一个个的去注册Service类,而是通过程序集的方式将所有的Service一起注册进来,而这些Service类又依赖Repository,所以也将对应的泛型形式注册进来,SqlUowDbContext是Repository要使用的,所以也注册进来。在测试的时候,使用了以下方式的代码,程序报错。原因是这里我们使用的是控制台程序,没有网络请求
builder.RegisterType<SqlUowDbContext>().As<ISqlUnitOfWork>().InstancePerRequest();
改正后的代码是
builder.RegisterType<SqlUowDbContext>().As<ISqlUnitOfWork>();
再编译运行,程序不在出错,打印出我们预先设定的内容,IOC依赖注入处理就此完毕。