EF Core: Eseguire SQL Query su un altro Database Come eseguire query SQL su database e/o tabelle non mappate con Entity Framework Core utilizzando i metodi HasNoKey e FromSqlRaw

BuildWebHost - Unable to create an object of type ApplicationDbContext error in EF Core 2.x - How to Fix

Se vi siete imbattuti in questo articolo, è probabile che siate sviluppatori ASP.NET Core al lavoro su un'applicazione web configurata per accedere a un database SQL tramite Entity Framework Core - il noto framework ORM (Object-Relational Mapping) open source per ADO. NET di cui abbiamo avuto più volte occasione di parlare in questo blog: in particolare, è probabile che abbiate l'esigenza di eseguire una query SQL su un database diverso da quello utilizzato da EF Core, e/o su tabelle diverse da quelle "mappate" dall'ORM.

Personalmente mi sono trovato ad affrontare questo problema in un paio di occasioni, ad esempio durante lo sviluppo di un'applicazione Web che utilizzava Serilog - un utile strumento che consente di persistere i log di ASP.NET all'interno di un database SQL: nel caso specifico, avevo configurato Serilog per scrivere i propri log su un database diverso rispetto a quello utilizzato da EF Core: la mia esigenza era quindi quella di trovare il modo di far accedere EF Core  alla tabella contenente i log record di Serilog, ovvero a un database diverso. In questo articolo spiegherò brevemente come sono riuscito in questo intento grazie ai metodi HasNoKey e FromSqlRaw forniti nativamente da EF Core.

Creare la Entity

La prima cosa che ho fatto è stata creare la entity che avrebbe ospitato i log record, così da poter gestire i suddetti in modo "strutturato": ho chiamato la entity BaseLog in quanto "Log" mi sembrava un nome un pò troppo generico, così da non rischiare di avere problemi di naming conflict che mi avrebbero costretto a specificare il namespace.

Come si può vedere, la entity non ha nulla di particolare: l'unica differenza di rilievo rispetto alle altre è data dal fatto che, come sappiamo, non deve esser mappata su una tabella del Database, in quanto si riferisce a una tabella presente su un database diverso. Questo significa che dovremo configurarla in modo diverso all'interno del nostro DbContext.

Configurare il DbContext

Come probabilmente già sapete (se utilizzate EF Core), il DbContext è una componente fondamentamentale di EF Core: in estrema sintesi, ciascuna istanza DbContext rappresenta una sessione all'interno di un database che può essere utilizzata per effettuare query di lettura e/o scrittura.

Per maggiori informazioni sul DbContext consigliamo di consultare la documentazione ufficiale su Microsoft Docs.

Il metodo standard per configurare una entity mappata su una tabella del database gestito tramite EF Core consiste nell'inserire le seguenti righe nel metodo OnModelCreating della nostra classe DbContext.cs:

Le righe di codice di cui sopra dicono a EF Core di eseguire il mapping dell'entità SampleEntity alla tabella del database [SampleEntity], eventualmente anche creandola (nel caso in cui si utilizzi l'approccio Code-First basato sulle migrations) se non esiste ancora; inoltre, la collezione SampleEntities ci consente anche accedere ai record della tabella utilizzando le comodissime fluent API di EF Core, come nell'esempio seguente:

Tuttavia, la entity BaseLog che abbiamo appena creato non deve essere "mappata"  su una determinata tabella del database utilizzato da EF Core, in quanto la tabella contenente i record  che dovrà gestire appartiene a un database diverso. Proprio per questo motivo dobbiamo adottare una configurazione leggermente diversa:

Come possiamo vedere, abbiamo configurato la entity in un modo leggermente diverso. In estrema sintesi, abbiamo detto a EF Core:

  • di mappare la entity BaseLog su una tabella con nome "Logs": questo mapping di fatto non sarà mai utilizzato, come vedremo tra poco: la sua unica funzione è quella di consentirci l'utilizzo dell'overload del metodo ToTable che ci consente di escludere le migration (vedi punto seguente).
  • di escludere in modo esplicito la entity BaseLog entity da qualsiasi migration, utilizzando il metodo ExcludeFromMigrations.
  • di configurare la entity senza chiave primaria, utilizzando il metodo HasNoKey.

A dire il vero, la tabella BaseLog una sua chiave ce l'ha eccome: si tratta del campo Id. Tuttavia, poiché tale chiave riguarda un database esterno ed è gestita direttamente tramite Serilog, è bene che EF Core la ignori del tutto.

Recuperare i dati

Ora che abbiamo definito la entity BaseLog e abbiamo detto a EF Core come gestirla correttamente, possiamo usarla nel modo seguente:

Come possiamo vedere, le attività di recupero dei dati sono gestite dal metodo FromSqlRaw, che conente di recuperare i dati da qualsiasi tabella non mappata in modo esplicito a un'entità all'interno di DbContext.

Nell'esempio sopra, Serilog è il nome del database esterno e Logs è il nome della tabella DB (nel database [Serilog]) contenente i record di log che vogliamo gestire con l'entità BaseLog: entrambi sono specificati utilizzando la variabile logsTable, il cui valore è "Serilog.Logs" (usando la tipica sintassi SQL DBName.TableName).

Inutile dire che, affinché il codice di cui sopra funzioni, sarà necessario concedere le autorizzazioni di lettura per il database [Serilog] allo stesso utente DB che stiamo utilizzando nella ConnectionString relativa a EF Core, altrimenti la query non funzionerà per ovvie ragioni legate alla mancanza di permessi/autorizzazioni sufficienti.

Conclusione

Per il momento è tutto: ci auguriamo che questo piccolo tutorial possa essere utile ad altri sviluppatori ASP.NET Core alla ricerca di un modo per utilizzare EF Core per gestire database e/o tabelle esterni senza rinunciare agli enormi vantaggi offerti dalle entity, dall'approccio fortemente tipizzato e dalle fluent API di EF Core.

 

About Ryan

IT Project Manager, Web Interface Architect e Lead Developer di numerosi siti e servizi web ad alto traffico in Italia e in Europa. Dal 2010 si occupa anche della progettazione di App e giochi per dispositivi Android, iOS e Mobile Phone per conto di numerose società italiane. Microsoft MVP for Development Technologies dal 2018.

View all posts by Ryan

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.