我正在ASP.NET Core WebAPI中创建计划的任务。我正在尝试从appsettings.json获取5个URL,这些URL不在一节中,而仅在appsettings.json的根目录中。如果我在Final类中对字符串进行硬编码(在该问题的最后一部分中提到),则可以正常工作,但是
这是我的 Program.cs:
public static void Main(string[] args)
{
var configuration = GetConfiguration();
var host = BuildWebHost(configuration, args);
host.Run();
//CreateWebHostBuilder(configuration,args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(IConfiguration _configuration,string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseConfiguration(_configuration);
private static IConfiguration GetConfiguration()
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddEnvironmentVariables();
return builder.Build();
}
以下是我的 Startup.cs:
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<ScheduledTaskSettings>(Configuration);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSingleton<IOrderSchedulerTask, OrderSchedulerTask>();
services.AddScheduler((sender, args) =>
{
Console.Write(args.Exception.Message);
args.SetObserved();
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
}
我还使用以下类创建了 ScheduledTaskSettings.cs 。字符串名称与 appsettings.json。
中的名称相同。public class ScheduledTaskSettings
{
public string ConnectionString { get; set; }
public string CustomersApiEndpointUrl { get; set; }
public string SubscriptionsApiEndpointUrl { get; set; }
public string ProductsApiEndpointUrl { get; set; }
public string SubscribersApiEndpointUrl { get; set; }
public string CronScheduleTime { get; set; }
}
我有一个界面 IOrderSchedulerTask.cs:
public interface IOrderSchedulerTask
{
string Schedule { get; }
Task ExecuteAsync(CancellationToken cancellationToken);
}
最后一个类OrderSchedulerTask.cs
public class OrderSchedulerTask : IOrderSchedulerTask
{
// Implementing IOrderScheduler
// Scheduled for every one minute
public string Schedule => "*/1 * * * *";
public async Task ExecuteAsync(CancellationToken cancellationToken)
{
var httpClient = new HttpClient();
OrderScheduler.GenerateOrders();
}
}
public class OrderScheduler
{
public static ScheduledTaskSettings _scheduledTaskSettngs;
public OrderScheduler(ScheduledTaskSettings scheduledTaskSettings)
{
_scheduledTaskSettngs = scheduledTaskSettings;
}
private static string CustomerApiUrl = _scheduledTaskSettngs.CustomersApiEndpointUrl;
private static string SubscriptionApiUrl = _scheduledTaskSettngs.SubscriptionsApiEndpointUrl;
private static string CatalogApiUrl = _scheduledTaskSettngs.ProductsApiEndpointUrl;
private static string SubscriberUrl = _scheduledTaskSettngs.SubscribersApiEndpointUrl;
private static string _connectionString = _scheduledTaskSettngs.ConnectionString;
private static List<Customer> customers = null;
private static List<Subscription> subscriptions = null;
private static List<CatalogItem> products = null;
private static List<Subscriber> subscribers = null;
private static List<Order> orders = new List<Order>();
/// <summary>
/// Generate the orders for next day
/// </summary>
public static async void GenerateOrders()
{
// Get Customers Data
GetCustomers();
//Get Subscription Data
GetSubscriptions();
//Get Products Data
GetProducts();
//Get Subscribers Data
GetSubscribers();
var order = new Order();
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
foreach (var item in subscriptions)
{
//
}
}
}
private static async void GetCustomers()
{
//Uses CustomerApiUrl String
}
private static async void GetSubscriptions()
{
//Uses SubscriptionApiUrl String
}
private static async void GetProducts()
{
//Uses CatalogApiUrl String
}
private static async void GetSubscribers()
{
//Uses SubscriberUrl String
}
}
如果我对字符串进行硬编码,则可以正常工作,但是当我使用配置方法时,它将引发以下异常:
Unhandled Exception:
Unhandled Exception:
Unhandled Exception:
Unhandled Exception: System.TypeInitializationException: The type initializer for
'Ordering.ScheduledTask.Tasks.OrderScheduler' threw an exception. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at Ordering.ScheduledTask.Tasks.OrderScheduler..cctor() in ... OrderSchedulerTask.cs Line 22
有人可以指导我解决我应该做些什么。
答案 0 :(得分:2)
我的代码存在一些问题:
为什么要在ConfigurationBuilder
中创建自定义Program.cs
?您使用WebHost.CreateDefaultBuilder
创建的默认构建器将已经设置了同时使用appsettings.json
和appsettings.<Environment>.json
以及其他资源(例如环境变量或命令行参数)的配置。
因此无需创建自定义配置,除非您确实有充分的理由,否则我建议您不要创建一个。
services.Configure<ScheduledTaskSettings>(Configuration)
是Options framework的一种方法。您可以这样配置IOptions<ScheduledTaskSettings>
。因此,为了使用该功能,您必须将IOptions<ScheduledTaskSettings>
注入您的OrderScheduler
中;不只是ScheduledTaskSettings
。
在OrderScheduler
中,您注入了任务设置(同样,这应该是选项),但是您可以使用它来静态设置_scheduledTaskSettngs
,该设置将被共享在所有实例之间。在使用DI的应用程序中,通常这是一个坏主意:您明确希望它保留为 instance 成员,以便它属于对象的生存期。
最终导致您出错的是以下部分:
private static string CustomerApiUrl = _scheduledTaskSettngs.CustomersApiEndpointUrl;
private static string SubscriptionApiUrl = _scheduledTaskSettngs.SubscriptionsApiEndpointUrl;
private static string CatalogApiUrl = _scheduledTaskSettngs.ProductsApiEndpointUrl;
private static string SubscriberUrl = _scheduledTaskSettngs.SubscribersApiEndpointUrl;
private static string _connectionString = _scheduledTaskSettngs.ConnectionString;
由于这些都是静态字段,因此在首次使用 type 时将对其进行静态初始化。此时,尚未创建任何实例,因此静态_scheduledTaskSettngs
尚未初始化。因此,它是null
,解释了您获得的NullReferenceException
。
这种方法还可以防止您对配置的更改做出反应,因为这些值仅一次读取,然后静态存储。
您应该使OrderScheduler
为单例,并将其正确注入其他事物,以便可以将其用作具有明确定义的生存期的实例:
public class OrderScheduler
{
public readonly ScheduledTaskSettings _scheduledTaskSettings;
public OrderScheduler(IOptions<ScheduledTaskSettings> scheduledTaskSettings)
{
_scheduledTaskSettings = scheduledTaskSettings.Value;
}
// using read-only properties instead
private string CustomerApiUrl => _scheduledTaskSettngs.CustomersApiEndpointUrl;
private string SubscriptionApiUrl => _scheduledTaskSettngs.SubscriptionsApiEndpointUrl;
private string CatalogApiUrl => _scheduledTaskSettngs.ProductsApiEndpointUrl;
private string SubscriberUrl => _scheduledTaskSettngs.SubscribersApiEndpointUrl;
private string _connectionString => _scheduledTaskSettngs.ConnectionString;
// …
// instance members only, no “static”
}