整合了一个命令行程序的框架(1)
aop和定时任务
这次更新主要增加了审计日志还有集成了hangfire做定时任务
-
先来看审计日志部分,审计日志主要是使用了Windsor的动态代理功能.每一个添加了Audited特性的方法或者类,在方法被调用时会输出参数和值.使用时只要在需要审计的service上增加Audited特性即可
[Audited] public class DemoService : LunaServiceBase, IDemoService { public string GetMessage(string name) { Logger.Info($"GetMessage {name}"); return "测试"; } public string GetDemo(DemoModel model) { var msg = $"{model.Name}: {model.Age}"; return msg; } }
同时在start上增加了一个可选参数.目前只有一个设置项,就是是否开启审计日志功能.如果不开启的话,标记了aduited的service也不会输出审计日志
-
定时任务的用法和之前的还是有一些区别.如要体现在runner上.之前的runner要继承LunaRunnerBase,如果使用hangfire的话要继承LunaHangfireRunnerBase.同时在run方法里增加定时任务
public class Runner : LunaHangfireRunnerBase { public override void Run() { RecurringJob.AddOrUpdate<IJobService>(service => service.OutputLog(), Cron.Minutely); } }
启动的方法还是和之前一样的使用starter即可.这里有一点需要注意一下.一定要在调用starter的run方法之前配置好hangfire
GlobalConfiguration.Configuration.UseSqlServerStorage("default");
这里推荐使用topshelf之类的框架把程序搞成服务
代码
和上一版对比的话,这次主要的变更在starter类构造函数中
private Starter(Type runnerType, StarterOption option)
{
Container = new WindsorContainer();
Container.Kernel.ComponentRegistered += (key, handler) =>
{
if (option.DisableAudit) return;
if (handler.ComponentModel.Implementation.IsDefined(typeof(AuditedAttribute), true))
{
handler.ComponentModel.Interceptors.Add(new InterceptorReference(typeof(AuditingInterceptor)));
}
};
Container.Register(
Classes.FromAssemblyInThisApplication(runnerType.Assembly)
.IncludeNonPublicTypes()
.BasedOn<ITransientDependency>()
.WithServiceAllInterfaces()
.If(type => !type.IsGenericTypeDefinition)
.WithService.Self()
.WithService.DefaultInterfaces()
.LifestyleTransient()
);
Container.Register(
Classes.FromAssemblyInThisApplication(runnerType.Assembly)
.IncludeNonPublicTypes()
.BasedOn<ISingletonDependency>()
.If(type => !type.IsGenericTypeDefinition)
.WithService.Self()
.WithService.DefaultInterfaces()
.LifestyleSingleton()
);
Container.Register(
Classes.FromAssemblyInThisApplication(runnerType.Assembly)
.IncludeNonPublicTypes()
.BasedOn<IInterceptor>()
.If(type => !type.IsGenericTypeDefinition)
.WithService.Self()
.LifestyleTransient()
);
Container.Register(
Component.For<Starter>().Instance(this).LifestyleSingleton()
);
}
新增注册了ComponentRegistered事件.在组件注册ioc完成时检测了组件是否声明了AuditedAttribute特性,如果有声明的话就会给组件增加一个拦截器实现审计日志.
另外就是把starter自己也注册进了ioc,这其实是为了替换hangfire的JobActivator时使用ioc容器.
public abstract class LunaHangfireRunnerBase : LunaRunnerBase
{
private BackgroundJobServer _backgroundJobServer;
public Starter Starter { get; set; }
public override void Init()
{
base.Init();
JobActivator.Current = new WindsorJobActivator(Starter.Container.Kernel);
_backgroundJobServer = new BackgroundJobServer();
}
public override void Stop()
{
_backgroundJobServer.Dispose();
}
}
可以看到LunaHangfireRunnerBase类的属性注入了starter.本来考虑构造注入的,但是使用构造注入的话,继承这个类的runner在代码上就会看起来不是很纯洁,所以放弃了
GitHub: https://github.com/lun3322/Luna.Service
这里有完整的代码和例子,没写单元测试大家凑合看吧.欢迎star
NuGet1: Install-Package Luna.Service
NuGet2: Install-Package Luna.Service.Nlog
NuGet2: Install-Package Luna.Service.Hangfire