Ciao,
ok... io però in genere preferisco scrivere codice riutilizzabile, quindi farò a modo mio.
Ovviamente la prima cosa da fare è recuperare il json dal url di riferimento, ne copio un singolo elemento e lo incollo in visual studio tramite incolla speciale, in questo modo per chi non lo sapesse già (per eventuali posteri) ottengo la classe bella pronta, diciamo che la chiamiamo "Provinciale" (raccoglie i dati delle provincie).
codice:
[Table("Provinciale")]
public class Provinciale : EntityBase
{
public DateTime data { get; set; }
public string stato { get; set; }
public int codice_regione { get; set; }
public string denominazione_regione { get; set; }
public int codice_provincia { get; set; }
public string denominazione_provincia { get; set; }
public string sigla_provincia { get; set; }
public float lat { get; set; }
public float _long { get; set; }
public int totale_casi { get; set; }
public string note_it { get; set; }
public string note_en { get; set; }
}
EntityBase è una classe "di comodità", infatti aggiunge il campo Id necessario per il contesto dati. Inoltre uso l'attributo table per dare un nome alla tabella interna al file. Ultima nota è importante che erediti EntityBase per poterlo usare come tipo generico in ReaderContex.
Una volta ottenuta la classe creo la web api e come sorgente dati uso EntityFramework core con SQLite, in modo da creare un file dati (basta cambiare la connessione e puoi utilizzare il db che ti pare se non vuoi usare un file).
Esempio:
codice:
[Route("covid19/[controller]")]
[ApiController]
public class ProvincialeController : ControllerBase
{
const string URL = "https://raw.githubusercontent.com/pcm-dpc/COVID-19/master/dati-json/dpc-covid19-ita-province-latest.json";
readonly string _databasePath = Path.Combine(Startup.contentRoot, "Provinciale.db");
readonly ReaderContex<Provinciale> _context;
public ProvincialeController()
{
_context = new ReaderContex<Provinciale>(_databasePath);
}
[HttpGet]
[EnableQuery]
public IQueryable<Provinciale> Get()
{
ReaderContex<Provinciale>.Update(URL, _databasePath, (ctx, lastUpdate, infoList) =>
{
ctx.Info.RemoveRange(ctx.Info);//massimo qualche centinaio di record (forse un migliaio), diversamente: ctx.Database.ExecuteSqlCommand("DELETE FROM [Provinciale]");
ctx.Info.AddRange(infoList);
return true;
});
return _context.Info;
}
Qui in realtà non c'è molto... Creo appunto un ReaderContex<Provinciale>, questo dentro contiene tre tabelle, una per impostare un log che traccia tutte le chiamate fatte alla sorgente json, indicando data e esito della chiama (se ha fatto aggiornamento o no). Poi una seconda tabella che indica le fasce orarie (da tale ora a tale ora) e con che frequenza di tempo deve aggiornare in quella fascia oraria.
Infine abbiamo la tabella delle info, che saranno quelle che verranno passate alla webapi. La struttura di quest'ultima ovviamente dipende dal tipo generico passato, in questo caso il tipo è "Provinciale".
Nel metodo che fornisce i dati della webapi chiamiamo il metodo statico Update passando l'URL della sorgente json, il path del file db e una Function che mi passa il contesto dati, la data dell'ultimo aggiornamento, e i dati json appena letti (viene chiamato in base alle fasce orarie, e solo quando è necessario aggiornare i dati dalla fonte), a quel punto decidi tu cosa fare con i dati (ad esempio aggiungere solo gli ultimi record oppure cancelli i vecchi ed aggiungi i nuovi in modo che sei sicuro che sono sempre aggiornati anche i vecchi), infine restituisci true se ritieni che debba essere segnato il nuovo log (ultima chiamata fatta).
Un altro esempio d'uso cambiando la struttura del json (uso il riepilogo nazionale):
codice:
[Route("covid19/[controller]")]
[ApiController]
public class NazionaleController : ControllerBase
{
const string URL = "https://raw.githubusercontent.com/pcm-dpc/COVID-19/master/dati-json/dpc-covid19-ita-andamento-nazionale.json";
readonly string _databasePath = Path.Combine(Startup.contentRoot, "Nazionale.db");
private readonly ReaderContex<Nazionale> _context;
public NazionaleController(){ _context = new ReaderContex<Nazionale>(_databasePath);}
[HttpGet]
[EnableQuery]
public IQueryable<Nazionale> Get()
{
ReaderContex<Nazionale>.Update(URL, _databasePath, (ctx, lastUpdate, infoList) =>{
ctx.Info.RemoveRange(ctx.Info);//massimo qualche centinaio di record, diversamente: ctx.Database.ExecuteSqlCommand("DELETE FROM [Nazionale]");
ctx.Info.AddRange(infoList);
return true;});return _context.Info;
}
[Table("Nazionale")]
public class Nazionale : EntityBase
{
public DateTime data { get; set; }
public string stato { get; set; }
public int ricoverati_con_sintomi { get; set; }
public int terapia_intensiva { get; set; }
public int totale_ospedalizzati { get; set; }
public int isolamento_domiciliare { get; set; }
public int totale_positivi { get; set; }
public int variazione_totale_positivi { get; set; }
public int nuovi_positivi { get; set; }
public int dimessi_guariti { get; set; }
public int deceduti { get; set; }
public int totale_casi { get; set; }
public int tamponi { get; set; }
public string note_it { get; set; }
public string note_en { get; set; }
}
}
Cambia ben poco come vedi... in realtà solo i vari path (del json e del file db) e ovviamente il tipo generato a partire dal json (puoi usare sostanzialmente qualsiasi json)
Ora chiamando covid19/nazionale o covid/provinciale puoi (mettendo il nuget OData) utilizzare i filtri in querystring, tipo covid19/nazionale?$filter=Id eq 1.
Mentre per gestire le fasce orarie e la relativa frequenza di aggiornamento aggiungi o rimuovi dal ReaderContex tipi TimeConfiguration, ognuna di esse è una singola fascia oraria.
Nel prossimo post il codice di ReaderContex.