从ASP.NET Core WebAPI中的appsettings.json获取一些值

时间:2019-12-08 10:52:47

标签: c# asp.net-core configuration asp.net-core-webapi

我正在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

有人可以指导我解决我应该做些什么。

1 个答案:

答案 0 :(得分:2)

我的代码存在一些问题:

  1. 为什么要在ConfigurationBuilder中创建自定义Program.cs?您使用WebHost.CreateDefaultBuilder创建的默认构建器将已经设置了同时使用appsettings.jsonappsettings.<Environment>.json以及其他资源(例如环境变量或命令行参数)的配置。

    因此无需创建自定义配置,除非您确实有充分的理由,否则我建议您不要创建一个。

  2. services.Configure<ScheduledTaskSettings>(Configuration)Options framework的一种方法。您可以这样配置IOptions<ScheduledTaskSettings>。因此,为了使用该功能,您必须将IOptions<ScheduledTaskSettings>注入您的OrderScheduler中;不只是ScheduledTaskSettings

  3. 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”
}