gpt4 book ai didi

c# - 使用 "Object Reference not set to an instance of an object"运行 Quartz 作业时出错

转载 作者:可可西里 更新时间:2023-11-01 09:12:41 31 4
gpt4 key购买 nike

我在 Global.asax.cs

中的 Application_Start 期间设置了一个 Quartz 作业
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
Logger.log("About to Setup Retry Job");
JobScheduler.Start();
}

这会调用 Start 方法,然后安排作业。

作业每 20 秒运行一次并抛出异常。这是我的工作。

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.log("Executing Job"); 抛出异常。这是一个打开日志文件并写入的静态方法。此方法适用于我网站的其他任何地方。

这是异常消息:{“对象引用未设置到对象的实例。”}

InnerException 为 NULL。这是堆栈:

DarwinsShoppingCart.dll!DarwinsShoppingCart.SharedClasses.JobScheduler.RetrySyspro.Execute(Quartz.IJobExecutionContext context) Line 69 C#
Quartz.dll!Quartz.Core.JobRunShell.Run(System.Threading.CancellationToken cancellationToken) Unknown
mscorlib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start<Quartz.Core.JobRunShell.<Run>d__9>(ref Quartz.Core.JobRunShell.<Run>d__9 stateMachine) Unknown
Quartz.dll!Quartz.Core.JobRunShell.Run(System.Threading.CancellationToken cancellationToken) Unknown
Quartz.dll!Quartz.Core.QuartzSchedulerThread.Run.AnonymousMethod__0() Unknown
mscorlib.dll!System.Threading.Tasks.Task<System.Threading.Tasks.Task>.InnerInvoke() Unknown
mscorlib.dll!System.Threading.Tasks.Task.Execute() Unknown
mscorlib.dll!System.Threading.Tasks.Task.ExecutionContextCallback(object obj) Unknown
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.Tasks.Task.ExecuteWithThreadLocal(ref System.Threading.Tasks.Task currentTaskSlot) Unknown
mscorlib.dll!System.Threading.Tasks.Task.ExecuteEntry(bool bPreventDoubleExecution) Unknown
mscorlib.dll!System.Threading.Tasks.Task.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem() Unknown
mscorlib.dll!System.Threading.ThreadPoolWorkQueue.Dispatch() Unknown
mscorlib.dll!System.Threading._ThreadPoolWaitCallback.PerformWaitCallback() Unknown

这是我的 Logger 类代码

public static void log(string strLog)
{
StreamWriter log;
FileStream fileStream = null;
DirectoryInfo logDirInfo = null;
FileInfo logFileInfo;
string username = Environment.UserName;
string logFilePath = HttpContext.Current.Server.MapPath("~/log/Log.txt");
logFileInfo = new FileInfo(logFilePath);
logDirInfo = new DirectoryInfo(logFileInfo.DirectoryName);
double fileSize = ConvertBytesToMegabytes(logFileInfo.Length);
if (fileSize > 30)
{
string FileDate = DateTime.Now.ToString().Replace("/", "-").Replace(":", "-");
string oldfilepath = HttpContext.Current.Server.MapPath("~/log/log-" + FileDate + ".txt");
File.Move(logFileInfo.FullName, oldfilepath);
}
if (!logFileInfo.Exists)
{
fileStream = logFileInfo.Create();
}
else
{
fileStream = new FileStream(logFilePath, FileMode.Append);
}
log = new StreamWriter(fileStream);

log.WriteLine(DateTime.Now.ToString("MM-dd HH:mm:ss") + " " + username + " " + strLog);
log.Close();
}

最佳答案

鉴于记录器可以在请求内调用,也可以在请求外调用。如果作业已启动且没有请求 HttpContext 将为 null

考虑将记录器与 HttpContext 等实现问题分离,并抽象映射路径和利用依赖注入(inject)的过程

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() requires an HttpContext while HostingEnvironment.MapPath does not.

引用 What is the difference between Server.MapPath and HostingEnvironment.MapPath?

实现可以尝试检查 null 的上下文,但 Server.MapPath() 最终会调用 HostingEnvironment.MapPath() 所以最好只使用 HostingEnvironment.MapPath()

public class PathProvider : IPathProvider {
public string MapPath(string path) {
return HostingEnvironment.MapPath(path);
}
}

现在剩下的就是配置调度程序以允许依赖注入(inject),并由您决定要使用哪个 DI 框架。

创建一个继承自默认 Quartz.NET 作业工厂的作业工厂 SimpleJobFactory .这个新的工作工厂将在其构造函数中采用 IServiceProvider,并覆盖 SimpleJobFactory 提供的默认 NewJob() 方法。

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 依赖注入(inject)扩展,由于 .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 派生类中并将其提供给作业工厂即可将一个代码换成另一个代码。

并且通过清理代码中使代码产生异味的问题,您可以更轻松地管理它。

关于c# - 使用 "Object Reference not set to an instance of an object"运行 Quartz 作业时出错,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52359819/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com