Search code examples
asp.netasp.net-coredependency-injectioninterfacenuget-package

Create Nuget Packge Suppots Dependecy Injection and use Interface to be implemented


I am trying to create a weather Nuget package that can be used in any .net core project, the library should give me the current weather for a specific city that I chose in Celsius or Fahrenheit.

I will use the weatherapi.com to get the current weather.

The library should support the dependency injection (DI) throw a service called IGetCurrentWeather with one method that’s take a city name and optional parameter to chose whether I want the result in Celsius or Fahrenheit.

I should be able to register the library in the startup of any .NET CORE project and provide the necessary configurations like the Code below

builder.Services.AddWeatherApi(WeatherOptions=>
WeatherOptions.ApiKey= "d1398cba8a894feb9f7180821232602");

the interface (the apikey below is for example):

public interface IGetCurrentWeather
    {
        public async Task<string> GetCurrentWeather()
        {
            HttpClient client = new HttpClient();
            HttpResponseMessage response = await client.GetAsync("http://api.weatherapi.com/v1/current.json?key=d1398cba8a894feb9f7180821232602&q=London&aqi=no\r\n");
            response.EnsureSuccessStatusCode();
            string responseBody = await response.Content.ReadAsStringAsync();

            return responseBody;
        }
    }

AddWeather Function:

public static class WeatherApi
    {
        public static WeatherOptions Options { get; } //set only via Secret Manager
        public static IServiceCollection AddWeatherApi(this IServiceCollection services, Action<WeatherOptions> configure)
        {
            services.Configure(configure) ;


            return services;

        }

WeatherOptions:

public class WeatherOptions
    {
        public string ApiKey { get; set; }
    }

how can i use the ApiKey that i entered in startup.cs when call GetCurrentWeather() function to access weatherapi.com account? Any help highly appreciated.


Solution

  • Your attempted solution here is close. But in general, you should not put an actual implementation in an interface. You can think of an interface as a contract, it defines the operations that you expect any implementing class to be able to perform.

    You start off okay with this:

    builder.Services.AddWeatherApi(WeatherApiOptions =>
        WeatherOptions.ApiKey= "d1398cba8a894feb9f7180821232602");
    

    We should rename your interface, just for consistency's sake. And remove the implementation. So what you used to call IGetCurrentWeather should look like this:

    public interface IWeatherApi
    {
        public Task<string> GetCurrentWeather();
    }
    

    Then you need a class that implements the interface. It should receive the WeatherApiOptions via constructor injection:

    public class WeatherApi : IWeatherApi
    {
        readonly WeatherApiOptions _weatherApiOptions;
    
        public WeatherApi(IOptions<WeatherApiOptions> weatherApiOptions)
        {
            _weatherApiOptions = weatherApiOptions.Value;
        }
    
        public async Task<string> GetCurrentWeather()
        {
            HttpClient client = new HttpClient();
            HttpResponseMessage response = await client.GetAsync($"http://api.weatherapi.com/v1/current.json?key={_weatherApiOptions.ApiKey}&q=London&aqi=no\r\n");
            response.EnsureSuccessStatusCode();
            string responseBody = await response.Content.ReadAsStringAsync();
    
            return responseBody;
        }
    }
    

    Then, an extension method that helps wire up your class with the DI container. Notice that I renamed it to better describe what its purpose is

    public static class WeatherApiDependencyInjectionExtensions
    {
        public static IServiceCollection AddWeatherApi(this IServiceCollection services, Action<WeatherApiOptions> configure)
        {
            services.AddTransient<IWeatherApi, WeatherApi>();
            services.Configure(configure) ;
            return services;
        }
    }
    

    And rename your options class to make it clearer what its for:

    public class WeatherApiOptions
    {
        public string ApiKey { get; set; }
    }
    

    Also, I noticed you're using HttpClient directly. There's some downfalls associated with that. I recommend using Flurl instead.