Skip to main content
C# Integration

C# Oil Price API

Integrate real-time oil and commodity prices into your .NET applications. Enterprise-ready with ASP.NET Core, Entity Framework, dependency injection, and async patterns.

Quick Start

1. Install NuGet Packages

# Basic HTTP client
dotnet add package System.Text.Json
# ASP.NET Core
dotnet add package Microsoft.AspNetCore.App
# Entity Framework Core
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

2. C# Features

  • Modern C# with nullable reference types
  • Async/await throughout
  • Built-in dependency injection

Basic Integration

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

public class OilPriceAPI
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private const string BaseUrl = "https://api.oilpriceapi.com";
    
    public OilPriceAPI(string apiKey)
    {
        _apiKey = apiKey ?? throw new ArgumentNullException(nameof(apiKey));
        
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Token {apiKey}");
        _httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json");
        _httpClient.Timeout = TimeSpan.FromSeconds(10);
    }
    
    public async Task<PriceResponse> GetBrentPriceAsync()
    {
        var response = await _httpClient.GetAsync($"{BaseUrl}/prices");
        response.EnsureSuccessStatusCode();
        
        var json = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<PriceResponse>(json);
    }
    
    public async Task<HealthResponse> GetCommodityHealthAsync()
    {
        var response = await _httpClient.GetAsync($"{BaseUrl}/health");
        response.EnsureSuccessStatusCode();
        
        var json = await response.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<HealthResponse>(json);
    }
    
    public async Task<CommodityPrice?> GetWTIPriceAsync()
    {
        try
        {
            var healthData = await GetCommodityHealthAsync();
            
            if (healthData?.Metrics?.CommodityHealth?.TryGetValue("WTI_USD", out var wtiData) == true)
            {
                return new CommodityPrice
                {
                    Price = wtiData.LatestPrice / 100.0, // Convert from cents
                    Timestamp = wtiData.LastUpdate,
                    Currency = "USD",
                    Unit = "barrel"
                };
            }
            
            return null;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error getting WTI price: {ex.Message}");
            return null;
        }
    }
    
    public async Task<Dictionary<string, CommodityPrice>> GetAllCommoditiesAsync()
    {
        var commodities = new Dictionary<string, CommodityPrice>();
        
        try
        {
            // Run both requests concurrently
            var brentTask = GetBrentPriceAsync();
            var healthTask = GetCommodityHealthAsync();
            
            await Task.WhenAll(brentTask, healthTask);
            
            // Process Brent data
            var brentResponse = await brentTask;
            if (brentResponse?.Data != null)
            {
                commodities["brent_crude"] = new CommodityPrice
                {
                    Price = brentResponse.Data.Price,
                    Timestamp = brentResponse.Data.Timestamp,
                    Currency = brentResponse.Data.Currency,
                    Unit = "barrel"
                };
            }
            
            // Process health data
            var healthResponse = await healthTask;
            if (healthResponse?.Metrics?.CommodityHealth != null)
            {
                foreach (var kvp in healthResponse.Metrics.CommodityHealth)
                {
                    var key = kvp.Key;
                    var data = kvp.Value;
                    
                    if (data.LatestPrice.HasValue)
                    {
                        var scale = IsCurrencyPair(key) ? 10000.0 : 100.0;
                        var price = data.LatestPrice.Value / scale;
                        
                        commodities[key.ToLower()] = new CommodityPrice
                        {
                            Price = price,
                            Timestamp = data.LastUpdate,
                            Currency = "USD",
                            Unit = GetUnit(key)
                        };
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error getting commodities: {ex.Message}");
        }
        
        return commodities;
    }
    
    private static bool IsCurrencyPair(string commodityKey) =>
        commodityKey is "EUR_USD" or "GBP_USD";
    
    private static string GetUnit(string commodityKey) => commodityKey switch
    {
        "WTI_USD" => "barrel",
        "NATURAL_GAS_USD" => "MMBtu",
        "GOLD_USD" => "ounce",
        "EUR_USD" or "GBP_USD" => "USD",
        _ => "unit"
    };
    
    public void Dispose()
    {
        _httpClient?.Dispose();
    }
}

// Data Transfer Objects
public class PriceResponse
{
    public string Status { get; set; }
    public PriceData Data { get; set; }
}

public class PriceData
{
    public double Price { get; set; }
    public string Timestamp { get; set; }
    public string Currency { get; set; }
    public string Unit { get; set; }
}

public class HealthResponse
{
    public string Status { get; set; }
    public Metrics Metrics { get; set; }
}

public class Metrics
{
    [JsonPropertyName("commodity_health")]
    public Dictionary<string, CommodityHealthData> CommodityHealth { get; set; }
}

public class CommodityHealthData
{
    [JsonPropertyName("latest_price")]
    public double? LatestPrice { get; set; }
    
    [JsonPropertyName("last_update")]
    public string LastUpdate { get; set; }
}

public class CommodityPrice
{
    public double Price { get; set; }
    public string Timestamp { get; set; }
    public string Currency { get; set; }
    public string Unit { get; set; }
    
    public override string ToString() =>
        $"{Price:F2} {Currency} per {Unit} (updated: {Timestamp})";
}

// Usage example
class Program
{
    static async Task Main(string[] args)
    {
        using var api = new OilPriceAPI("YOUR_API_KEY_HERE");
        
        try
        {
            Console.WriteLine("=== Live Oil Prices ===");
            
            // Get Brent crude price
            var brentData = await api.GetBrentPriceAsync();
            if (brentData?.Data != null)
            {
                Console.WriteLine($"Brent Crude: {brentData.Data.Price}/barrel");
            }
            
            // Get WTI price
            var wtiPrice = await api.GetWTIPriceAsync();
            if (wtiPrice != null)
            {
                Console.WriteLine($"WTI Crude: {wtiPrice}");
            }
            
            // Get all commodities
            var allCommodities = await api.GetAllCommoditiesAsync();
            foreach (var (name, price) in allCommodities)
            {
                Console.WriteLine($"{name.ToUpper()}: {price}");
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Error: {ex.Message}");
        }
    }
}

ASP.NET Core Web API

Production-ready ASP.NET Core service with caching, dependency injection, and background services:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using System.Text.Json;

// Program.cs (ASP.NET Core 6+)
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddMemoryCache();
builder.Services.AddHttpClient();

// Configure options
builder.Services.Configure<OilPriceApiOptions>(
    builder.Configuration.GetSection("OilPriceApi"));

// Register services
builder.Services.AddScoped<IOilPriceService, OilPriceService>();
builder.Services.AddHostedService<PriceUpdateService>();

// Add CORS
builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        policy.AllowAnyOrigin()
              .AllowAnyMethod()
              .AllowAnyHeader();
    });
});

var app = builder.Build();

app.UseCors();
app.UseRouting();
app.MapControllers();

app.Run();

// Configuration model
public class OilPriceApiOptions
{
    public string ApiKey { get; set; } = string.Empty;
    public string BaseUrl { get; set; } = "https://api.oilpriceapi.com";
    public int TimeoutSeconds { get; set; } = 10;
    public int CacheMinutes { get; set; } = 5;
}

// Service interface
public interface IOilPriceService
{
    Task<ApiResult<PriceResponse>> GetBrentPriceAsync();
    Task<ApiResult<HealthResponse>> GetCommodityHealthAsync();
    Task<ApiResult<Dictionary<string, CommodityPrice>>> GetAllCommoditiesAsync();
    Task<ApiResult<CommodityPrice>> GetCommodityPriceAsync(string commodity);
}

// Service implementation
public class OilPriceService : IOilPriceService
{
    private readonly HttpClient _httpClient;
    private readonly IMemoryCache _cache;
    private readonly OilPriceApiOptions _options;
    private readonly ILogger<OilPriceService> _logger;
    
    public OilPriceService(
        HttpClient httpClient,
        IMemoryCache cache,
        IOptions<OilPriceApiOptions> options,
        ILogger<OilPriceService> logger)
    {
        _httpClient = httpClient;
        _cache = cache;
        _options = options.Value;
        _logger = logger;
        
        ConfigureHttpClient();
    }
    
    private void ConfigureHttpClient()
    {
        _httpClient.BaseAddress = new Uri(_options.BaseUrl);
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Token {_options.ApiKey}");
        _httpClient.DefaultRequestHeaders.Add("Content-Type", "application/json");
        _httpClient.Timeout = TimeSpan.FromSeconds(_options.TimeoutSeconds);
    }
    
    public async Task<ApiResult<PriceResponse>> GetBrentPriceAsync()
    {
        const string cacheKey = "brent_price";
        
        if (_cache.TryGetValue(cacheKey, out PriceResponse cachedPrice))
        {
            return ApiResult<PriceResponse>.Success(cachedPrice);
        }
        
        try
        {
            var response = await _httpClient.GetAsync("/prices");
            
            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                var priceResponse = JsonSerializer.Deserialize<PriceResponse>(json);
                
                // Cache the result
                _cache.Set(cacheKey, priceResponse, TimeSpan.FromMinutes(_options.CacheMinutes));
                
                return ApiResult<PriceResponse>.Success(priceResponse);
            }
            
            _logger.LogError("Failed to fetch Brent price: {StatusCode}", response.StatusCode);
            return ApiResult<PriceResponse>.Failure($"HTTP {response.StatusCode}");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error fetching Brent price");
            return ApiResult<PriceResponse>.Failure(ex.Message);
        }
    }
    
    public async Task<ApiResult<HealthResponse>> GetCommodityHealthAsync()
    {
        const string cacheKey = "commodity_health";
        
        if (_cache.TryGetValue(cacheKey, out HealthResponse cachedHealth))
        {
            return ApiResult<HealthResponse>.Success(cachedHealth);
        }
        
        try
        {
            var response = await _httpClient.GetAsync("/health");
            
            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                var healthResponse = JsonSerializer.Deserialize<HealthResponse>(json);
                
                // Cache the result
                _cache.Set(cacheKey, healthResponse, TimeSpan.FromMinutes(_options.CacheMinutes));
                
                return ApiResult<HealthResponse>.Success(healthResponse);
            }
            
            _logger.LogError("Failed to fetch commodity health: {StatusCode}", response.StatusCode);
            return ApiResult<HealthResponse>.Failure($"HTTP {response.StatusCode}");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error fetching commodity health");
            return ApiResult<HealthResponse>.Failure(ex.Message);
        }
    }
    
    public async Task<ApiResult<Dictionary<string, CommodityPrice>>> GetAllCommoditiesAsync()
    {
        const string cacheKey = "all_commodities";
        
        if (_cache.TryGetValue(cacheKey, out Dictionary<string, CommodityPrice> cachedCommodities))
        {
            return ApiResult<Dictionary<string, CommodityPrice>>.Success(cachedCommodities);
        }
        
        try
        {
            var brentTask = GetBrentPriceAsync();
            var healthTask = GetCommodityHealthAsync();
            
            await Task.WhenAll(brentTask, healthTask);
            
            var commodities = new Dictionary<string, CommodityPrice>();
            
            // Process Brent data
            var brentResult = await brentTask;
            if (brentResult.IsSuccess && brentResult.Data?.Data != null)
            {
                commodities["brent_crude"] = new CommodityPrice
                {
                    Price = brentResult.Data.Data.Price,
                    Timestamp = brentResult.Data.Data.Timestamp,
                    Currency = brentResult.Data.Data.Currency,
                    Unit = "barrel"
                };
            }
            
            // Process health data
            var healthResult = await healthTask;
            if (healthResult.IsSuccess && healthResult.Data?.Metrics?.CommodityHealth != null)
            {
                foreach (var (key, data) in healthResult.Data.Metrics.CommodityHealth)
                {
                    if (data.LatestPrice.HasValue)
                    {
                        var scale = IsCurrencyPair(key) ? 10000.0 : 100.0;
                        var price = data.LatestPrice.Value / scale;
                        
                        commodities[key.ToLower()] = new CommodityPrice
                        {
                            Price = price,
                            Timestamp = data.LastUpdate,
                            Currency = "USD",
                            Unit = GetUnit(key)
                        };
                    }
                }
            }
            
            // Cache the result
            _cache.Set(cacheKey, commodities, TimeSpan.FromMinutes(_options.CacheMinutes));
            
            return ApiResult<Dictionary<string, CommodityPrice>>.Success(commodities);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error fetching all commodities");
            return ApiResult<Dictionary<string, CommodityPrice>>.Failure(ex.Message);
        }
    }
    
    public async Task<ApiResult<CommodityPrice>> GetCommodityPriceAsync(string commodity)
    {
        var allCommoditiesResult = await GetAllCommoditiesAsync();
        
        if (!allCommoditiesResult.IsSuccess)
        {
            return ApiResult<CommodityPrice>.Failure(allCommoditiesResult.ErrorMessage);
        }
        
        var commodityKey = commodity.ToLower();
        if (allCommoditiesResult.Data.TryGetValue(commodityKey, out var price))
        {
            return ApiResult<CommodityPrice>.Success(price);
        }
        
        return ApiResult<CommodityPrice>.Failure($"Commodity '{commodity}' not found");
    }
    
    private static bool IsCurrencyPair(string commodityKey) =>
        commodityKey is "EUR_USD" or "GBP_USD";
    
    private static string GetUnit(string commodityKey) => commodityKey switch
    {
        "WTI_USD" => "barrel",
        "NATURAL_GAS_USD" => "MMBtu",
        "GOLD_USD" => "ounce",
        "EUR_USD" or "GBP_USD" => "USD",
        _ => "unit"
    };
}

// API Controller
[ApiController]
[Route("api/[controller]")]
public class OilPricesController : ControllerBase
{
    private readonly IOilPriceService _oilPriceService;
    private readonly ILogger<OilPricesController> _logger;
    
    public OilPricesController(IOilPriceService oilPriceService, ILogger<OilPricesController> logger)
    {
        _oilPriceService = oilPriceService;
        _logger = logger;
    }
    
    [HttpGet]
    public async Task<ActionResult<Dictionary<string, CommodityPrice>>> GetAllPrices()
    {
        var result = await _oilPriceService.GetAllCommoditiesAsync();
        
        if (result.IsSuccess)
        {
            return Ok(new
            {
                status = "success",
                data = result.Data,
                timestamp = DateTime.UtcNow
            });
        }
        
        _logger.LogError("Failed to get all prices: {Error}", result.ErrorMessage);
        return StatusCode(500, new { status = "error", message = result.ErrorMessage });
    }
    
    [HttpGet("{commodity}")]
    public async Task<ActionResult<CommodityPrice>> GetCommodityPrice(string commodity)
    {
        var result = await _oilPriceService.GetCommodityPriceAsync(commodity);
        
        if (result.IsSuccess)
        {
            return Ok(new
            {
                status = "success",
                commodity = commodity,
                data = result.Data,
                timestamp = DateTime.UtcNow
            });
        }
        
        if (result.ErrorMessage.Contains("not found"))
        {
            return NotFound(new { status = "error", message = result.ErrorMessage });
        }
        
        _logger.LogError("Failed to get {Commodity} price: {Error}", commodity, result.ErrorMessage);
        return StatusCode(500, new { status = "error", message = result.ErrorMessage });
    }
    
    [HttpGet("brent")]
    public async Task<ActionResult<PriceResponse>> GetBrentPrice()
    {
        var result = await _oilPriceService.GetBrentPriceAsync();
        
        if (result.IsSuccess)
        {
            return Ok(result.Data);
        }
        
        _logger.LogError("Failed to get Brent price: {Error}", result.ErrorMessage);
        return StatusCode(500, new { status = "error", message = result.ErrorMessage });
    }
}

// Background service for periodic updates
public class PriceUpdateService : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<PriceUpdateService> _logger;
    
    public PriceUpdateService(IServiceProvider serviceProvider, ILogger<PriceUpdateService> logger)
    {
        _serviceProvider = serviceProvider;
        _logger = logger;
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                using var scope = _serviceProvider.CreateScope();
                var oilPriceService = scope.ServiceProvider.GetRequiredService<IOilPriceService>();
                
                _logger.LogInformation("Refreshing commodity prices...");
                var result = await oilPriceService.GetAllCommoditiesAsync();
                
                if (result.IsSuccess)
                {
                    _logger.LogInformation("Successfully refreshed {Count} commodities", result.Data.Count);
                }
                else
                {
                    _logger.LogError("Failed to refresh prices: {Error}", result.ErrorMessage);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error in price update service");
            }
            
            // Wait 5 minutes before next update
            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
        }
    }
}

// Result wrapper
public class ApiResult<T>
{
    public bool IsSuccess { get; init; }
    public T Data { get; init; }
    public string ErrorMessage { get; init; } = string.Empty;
    
    public static ApiResult<T> Success(T data) =>
        new() { IsSuccess = true, Data = data };
    
    public static ApiResult<T> Failure(string error) =>
        new() { IsSuccess = false, ErrorMessage = error };
}

// appsettings.json
/*
{
  "OilPriceApi": {
    "ApiKey": "YOUR_API_KEY_HERE",
    "BaseUrl": "https://api.oilpriceapi.com",
    "TimeoutSeconds": 10,
    "CacheMinutes": 5
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  }
}
*/

Entity Framework Integration

Advanced implementation with Entity Framework for price history and alerts:

using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;

// Entity models for storing price history
public class PriceHistory
{
    public int Id { get; set; }
    
    [Required]
    [MaxLength(50)]
    public string Commodity { get; set; } = string.Empty;
    
    public double Price { get; set; }
    
    [MaxLength(10)]
    public string Currency { get; set; } = "USD";
    
    [MaxLength(20)]
    public string Unit { get; set; } = string.Empty;
    
    public DateTime Timestamp { get; set; }
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

public class PriceAlert
{
    public int Id { get; set; }
    
    [Required]
    [MaxLength(50)]
    public string Commodity { get; set; } = string.Empty;
    
    public double ThresholdPrice { get; set; }
    
    [Required]
    [MaxLength(10)]
    public string AlertType { get; set; } = string.Empty; // "above" or "below"
    
    [Required]
    [EmailAddress]
    public string Email { get; set; } = string.Empty;
    
    public bool IsActive { get; set; } = true;
    public DateTime? LastTriggered { get; set; }
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

// DbContext
public class OilPriceDbContext : DbContext
{
    public OilPriceDbContext(DbContextOptions<OilPriceDbContext> options) : base(options) { }
    
    public DbSet<PriceHistory> PriceHistory { get; set; }
    public DbSet<PriceAlert> PriceAlerts { get; set; }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<PriceHistory>(entity =>
        {
            entity.HasIndex(e => new { e.Commodity, e.Timestamp });
            entity.Property(e => e.Price).HasPrecision(18, 4);
        });
        
        modelBuilder.Entity<PriceAlert>(entity =>
        {
            entity.HasIndex(e => new { e.Commodity, e.IsActive });
            entity.Property(e => e.ThresholdPrice).HasPrecision(18, 4);
        });
    }
}

// Enhanced service with Entity Framework
public interface IPriceHistoryService
{
    Task SavePriceHistoryAsync(Dictionary<string, CommodityPrice> prices);
    Task<List<PriceHistory>> GetPriceHistoryAsync(string commodity, DateTime from, DateTime to);
    Task<List<PriceAlert>> CheckAlertsAsync(Dictionary<string, CommodityPrice> currentPrices);
}

public class PriceHistoryService : IPriceHistoryService
{
    private readonly OilPriceDbContext _context;
    private readonly ILogger<PriceHistoryService> _logger;
    
    public PriceHistoryService(OilPriceDbContext context, ILogger<PriceHistoryService> logger)
    {
        _context = context;
        _logger = logger;
    }
    
    public async Task SavePriceHistoryAsync(Dictionary<string, CommodityPrice> prices)
    {
        try
        {
            var priceHistories = prices.Select(kvp => new PriceHistory
            {
                Commodity = kvp.Key,
                Price = kvp.Value.Price,
                Currency = kvp.Value.Currency,
                Unit = kvp.Value.Unit,
                Timestamp = DateTime.TryParse(kvp.Value.Timestamp, out var ts) ? ts : DateTime.UtcNow
            }).ToList();
            
            await _context.PriceHistory.AddRangeAsync(priceHistories);
            await _context.SaveChangesAsync();
            
            _logger.LogInformation("Saved {Count} price history records", priceHistories.Count);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error saving price history");
        }
    }
    
    public async Task<List<PriceHistory>> GetPriceHistoryAsync(string commodity, DateTime from, DateTime to)
    {
        return await _context.PriceHistory
            .Where(p => p.Commodity == commodity.ToLower() && p.Timestamp >= from && p.Timestamp <= to)
            .OrderBy(p => p.Timestamp)
            .ToListAsync();
    }
    
    public async Task<List<PriceAlert>> CheckAlertsAsync(Dictionary<string, CommodityPrice> currentPrices)
    {
        var triggeredAlerts = new List<PriceAlert>();
        
        try
        {
            var activeAlerts = await _context.PriceAlerts
                .Where(a => a.IsActive)
                .ToListAsync();
            
            foreach (var alert in activeAlerts)
            {
                if (currentPrices.TryGetValue(alert.Commodity.ToLower(), out var currentPrice))
                {
                    bool shouldTrigger = alert.AlertType.ToLower() switch
                    {
                        "above" => currentPrice.Price >= alert.ThresholdPrice,
                        "below" => currentPrice.Price <= alert.ThresholdPrice,
                        _ => false
                    };
                    
                    if (shouldTrigger)
                    {
                        alert.LastTriggered = DateTime.UtcNow;
                        triggeredAlerts.Add(alert);
                        
                        _logger.LogInformation(
                            "Alert triggered for {Commodity}: {Price} {AlertType} {Threshold}",
                            alert.Commodity, currentPrice.Price, alert.AlertType, alert.ThresholdPrice);
                    }
                }
            }
            
            if (triggeredAlerts.Any())
            {
                await _context.SaveChangesAsync();
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error checking price alerts");
        }
        
        return triggeredAlerts;
    }
}

// Enhanced background service with persistence
public class EnhancedPriceUpdateService : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;
    private readonly ILogger<EnhancedPriceUpdateService> _logger;
    
    public EnhancedPriceUpdateService(IServiceProvider serviceProvider, ILogger<EnhancedPriceUpdateService> logger)
    {
        _serviceProvider = serviceProvider;
        _logger = logger;
    }
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                using var scope = _serviceProvider.CreateScope();
                var oilPriceService = scope.ServiceProvider.GetRequiredService<IOilPriceService>();
                var historyService = scope.ServiceProvider.GetRequiredService<IPriceHistoryService>();
                
                // Fetch current prices
                var result = await oilPriceService.GetAllCommoditiesAsync();
                
                if (result.IsSuccess && result.Data.Any())
                {
                    // Save price history
                    await historyService.SavePriceHistoryAsync(result.Data);
                    
                    // Check price alerts
                    var triggeredAlerts = await historyService.CheckAlertsAsync(result.Data);
                    
                    if (triggeredAlerts.Any())
                    {
                        _logger.LogInformation("Triggered {Count} price alerts", triggeredAlerts.Count);
                        // Here you could send emails, notifications, etc.
                    }
                    
                    _logger.LogInformation("Successfully processed {Count} commodities", result.Data.Count);
                }
                else
                {
                    _logger.LogError("Failed to fetch prices: {Error}", result.ErrorMessage);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error in enhanced price update service");
            }
            
            await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
        }
    }
}

Available Commodities

Brent Crude Oil

North Sea Brent crude oil, the global benchmark for oil pricing representing ~60% of internationally traded crude oil.

API Field: BRENT_CRUDE_USD
Unit: barrel
Endpoint: /prices

WTI Crude Oil

West Texas Intermediate crude oil, the primary benchmark for North American oil markets and NYMEX futures.

API Field: WTI_USD
Unit: barrel
Endpoint: /health

Dubai Crude Oil

Dubai crude oil benchmark for Middle Eastern crude oil pricing and Asian markets.

API Field: DUBAI_CRUDE_USD
Unit: barrel
Endpoint: /health

Natural Gas

NYMEX Henry Hub natural gas futures, the primary pricing benchmark for North American natural gas markets.

API Field: NATURAL_GAS_USD
Unit: MMBtu
Endpoint: /health

Natural Gas (UK)

UK natural gas pricing in British pence per therm.

API Field: NATURAL_GAS_GBP
Unit: pence/therm
Endpoint: /health

Dutch TTF Gas

Dutch Title Transfer Facility (TTF) natural gas futures, the European gas pricing benchmark.

API Field: DUTCH_TTF_EUR
Unit: EUR/MWh
Endpoint: /health

Gasoline RBOB

Reformulated Blendstock for Oxygenate Blending (RBOB) gasoline futures.

API Field: GASOLINE_RBOB_USD
Unit: gallon
Endpoint: /health

Heating Oil

No. 2 heating oil futures, a key refined petroleum product for heating and industrial use.

API Field: HEATING_OIL_USD
Unit: gallon
Endpoint: /health

Coal

Coal futures pricing for thermal coal used in power generation.

API Field: COAL_USD
Unit: ton
Endpoint: /health

Gold

Gold futures pricing, a key precious metal and safe-haven asset.

API Field: GOLD_USD
Unit: ounce
Endpoint: /health

EUR/USD

Euro to US Dollar exchange rate, the most traded currency pair globally.

API Field: EUR_USD
Unit: USD
Endpoint: /health

GBP/USD

British Pound to US Dollar exchange rate, also known as "Cable".

API Field: GBP_USD
Unit: USD
Endpoint: /health

C# Best Practices

Async Patterns

  • Use async/await throughout the call stack
  • Configure HttpClient with proper timeouts
  • Implement proper cancellation tokens

Enterprise Features

  • Use dependency injection container
  • Implement structured logging
  • Add health checks and monitoring

Ready to Start Building?

Get your free API key and start integrating real-time commodity prices into your C# .NET applications today.