我有一个import sys
sys.version_info = "boo"
print(sys.version_info) # boo
sys.modules.pop("sys")
import sys # reloaded
print(sys.version_info)
# Output: sys.version_info(major=3, minor=6, ...
的{{1}}期间设置的Quartz作业
Application_Start
这将调用Global.asax.cs
方法,该方法然后计划作业。
作业每20秒运行一次,并引发异常。这是我的工作。
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
Logger.log("About to Setup Retry Job");
JobScheduler.Start();
}
此行Start
引发异常。这是一个“静态”方法,可打开一个日志文件并将其写入。此方法可在我网站的其他任何地方使用。
以下是异常消息: {“对象引用未设置为对象的实例。”}
InnerException为NULL。这是堆栈:
public class RetryTempJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
try
{
Logger.log("Executing Job");
new ProcessOrder().retryFailedOrders();
//Logger.log("Done Executing Syspro Job");
await Console.Error.WriteLineAsync("Done Executing Syspro Job");
}
catch (Exception se)
{
await Console.Error.WriteLineAsync("" + se.InnerException);
}
}
}
这是我的Logger类代码
Logger.log("Executing Job");
答案 0 :(得分:3)
鉴于记录器可以在请求内调用,也可以在请求外调用。如果作业开始并且没有请求,HttpContext
将为 null
考虑将记录器与HttpContext
之类的实现问题脱钩,并抽象出映射路径和利用依赖项注入的过程
public interface IPathProvider {
string MapPath(string path);
}
您还应该避免静态记录器代码的气味,因为这会使代码难以维护和孤立地进行测试。
public interface ILogger {
void log(string message);
//...
}
public class Logger : ILogger {
private readonly IPathProvider pathProvider;
public Logger(IPathProvider pathProvider) {
this.pathProvider = pathProvider;
}
public void log(string strLog) {
FileStream fileStream = null;
string username = Environment.UserName;
string logFilePath = pathProvider.MapPath("~/log/Log.txt");
var logFileInfo = new FileInfo(logFilePath);
var logDirInfo = new DirectoryInfo(logFileInfo.DirectoryName);
double fileSize = ConvertBytesToMegabytes(logFileInfo.Length);
if (fileSize > 30) {
string FileDate = DateTime.Now.ToString().Replace("/", "-").Replace(":", "-");
string oldfilepath = pathProvider.MapPath("~/log/log-" + FileDate + ".txt");
File.Move(logFileInfo.FullName, oldfilepath);
}
if (!logFileInfo.Exists) {
fileStream = logFileInfo.Create();
} else {
fileStream = new FileStream(logFilePath, FileMode.Append);
}
using(fileStream) {
using (var log = new StreamWriter(fileStream)) {
log.WriteLine(DateTime.Now.ToString("MM-dd HH:mm:ss") + " " + username + " " + strLog);
log.Close();
}
}
}
}
并使工作明确取决于抽象。
public class RetryTempJob : IJob {
private readonly ILogger logger;
public RetryTempJob(ILogger logger) {
this.logger = logger;
}
public async Task Execute(IJobExecutionContext context) {
try {
logger.log("Executing Job");
new ProcessOrder().retryFailedOrders();
//logger.log("Done Executing Syspro Job");
await Console.Error.WriteLineAsync("Done Executing Syspro Job");
} catch (Exception se) {
await Console.Error.WriteLineAsync("" + se.InnerException);
}
}
}
这里可以抽象出更多内容,但这超出了示例范围。
现在已经解决了设计问题,我们可以看看路径提供程序的实现来解决HttpContext
的情况。
Server.MapPath()
要求HttpContext
,而HostingEnvironment.MapPath
不需要。
引用What is the difference between Server.MapPath and HostingEnvironment.MapPath?
该实现可以尝试检查上下文是否为空,但是Server.MapPath()
最终会调用HostingEnvironment.MapPath()
,因此最好只使用HostingEnvironment.MapPath()
public class PathProvider : IPathProvider {
public string MapPath(string path) {
return HostingEnvironment.MapPath(path);
}
}
现在剩下的是配置调度程序以允许依赖项注入,并让您决定要使用哪个DI框架。
创建一个从默认Quartz.NET作业工厂SimpleJobFactory
继承的作业工厂。这个新的作业工厂将在其构造函数中使用IServiceProvider
,并覆盖NewJob()
提供的默认SimpleJobFactory
方法。
class MyDefaultJobFactory : SimpleJobFactory {
private readonly IServiceProvider container;
public MyDefaultJobFactory(IServiceProvider container) {
this.container = container;
}
public override IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) {
IJobDetail jobDetail = bundle.JobDetail;
Type jobType = jobDetail.JobType;
try {
// this will inject any dependencies that the job requires
return (IJob) this.container.GetService(jobType);
} catch (Exception e) {
var errorMessage = string.Format("Problem instantiating job '{0}'.", jobType.FullName);
throw new SchedulerException(errorMessage, e);
}
}
}
有许多DI框架可供选择,但是对于本示例,我使用的是.Net Core依赖注入扩展,由于.Net Core具有模块化性质,因此可以轻松地将其放入您的项目中。
最后,在应用程序启动时配置服务集合
protected void Application_Start() {
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
var services = new ServiceCollection();
var serviceProvider = ConfigureService(services);
var logger = serviceProvider.GetService<ILogger>();
logger.log("About to Setup Retry Job");
var jobScheduler = serviceProvider.GetRequiredService<IScheduler>();
//...add jobs as needed
jobScheduler.ScheduleJob(.....);
//and start scheduler
jobScheduler.Start();
}
private IServiceProvider ConfigureService(IServiceCollection services) {
//Add job dependencies
services.AddSingleton<ILogger, Logger>();
services.AddSingleton<IPathProvider, PathProvider>();
//add scheduler
services.AddSingleton<IScheduler>(sp => {
var scheduler = new StdSchedulerFactory().GetScheduler();
scheduler.JobFactory = new MyDefaultJobFactory(sp);
return scheduler;
});
//...add any other dependencies
//build and return service provider
return services.BuildServiceProvider();
}
正如我之前所说,您可以使用任何其他DI / IoC容器。现在,重构的代码足够灵活,您只需将其包装在派生的IServiceProvider
类中,然后将其提供给作业工厂即可将它们交换。
通过清除使代码变味的担忧,您可以更轻松地对其进行管理。
答案 1 :(得分:2)
使用Quarz Job时没有HttpContext
。它运行我一个单独的线程。因此,在具有Quarz Job的网站上,我使用HostingEnvironment
如此
HttpContext.Current.Server.MapPath("~/log/Log.txt")
使用
using System.Web.Hosting;
HostingEnvironment.MapPath("~/log/Log.txt");