Entity Framework: un database, più DbContexts. È una cattiva idea? [chiuso]


213

La mia impressione finora è stata che a DbContextsia destinato a rappresentare il tuo database e quindi, se la tua applicazione utilizza un database, ne vorresti solo uno DbContext.

Tuttavia, alcuni colleghi vogliono suddividere le aree funzionali in DbContextclassi separate .

Credo che questo provenga da un buon posto - il desiderio di mantenere il codice più pulito - ma sembra volatile. Il mio istinto mi sta dicendo che è una cattiva idea, ma sfortunatamente, il mio istinto non è una condizione sufficiente per una decisione di progettazione.

Quindi sto cercando:

A) esempi concreti del perché questa potrebbe essere una cattiva idea;

B) assicura che tutto andrà bene.


Risposte:


168

È possibile disporre di più contesti per un singolo database. Può essere utile, ad esempio, se il database contiene più schemi di database e si desidera gestirli ciascuno come area autonoma separata.

Il problema è quando si desidera utilizzare prima il codice per creare il database: solo un singolo contesto nell'applicazione può farlo. Il trucco per questo è di solito un contesto aggiuntivo contenente tutte le entità che viene utilizzato solo per la creazione di database. I contesti applicativi reali contenenti solo sottoinsiemi delle entità devono avere l'inizializzatore del database impostato su null.

Ci sono altri problemi che vedrai quando utilizzi più tipi di contesto - ad esempio tipi di entità condivisi e il loro passaggio da un contesto a un altro, ecc. Generalmente è possibile, può rendere il tuo progetto molto più pulito e separare aree funzionali diverse ma ha i suoi costi in ulteriore complessità.


21
L'uso di un singolo contesto per app può essere costoso se l'applicazione ha molte entità / tabelle. Pertanto, a seconda dello schema, potrebbe anche avere senso avere più contesti.
DarthVader

7
Dato che non mi iscrivo a Pluralsight, ho trovato questo fantastico articolo di Julie Lerman (il suo commento ) scritto bene dopo questo Q / A, ma molto appropriato: msdn.microsoft.com/en-us/magazine/jj883952.aspx
Dave T.

Suggerisco, framework di entità per supportare più dbcontexts nello stesso database nominando convenzione. Per questo motivo ho ancora scritto il mio ORM per scopi di applicazione modulari. È difficile da credere, costringe una singola applicazione a utilizzare un singolo database. Soprattutto nelle web farm hai un numero limitato di database
freewill del

Inoltre, mi sono reso conto che puoi abilitare le migrazioni solo per un contesto all'interno del progetto tramite PM Console.
Piotr Kwiatek,

9
@PiotrKwiatek Non sono sicuro che sia cambiato tra il tuo commento e ora, ma Enable-Migrations -ContextTypeName MyContext -MigrationsDirectory Migrations\MyContextMigrationsfunziona ora.
Zack,

60

Ho scritto questa risposta circa quattro anni fa e la mia opinione non è cambiata. Ma da allora ci sono stati sviluppi significativi sul fronte dei micro-servizi. Alla fine ho aggiunto note specifiche sui micro-servizi ...

Mi opporrò all'idea, con l'esperienza del mondo reale di sostenere il mio voto.

Sono stato portato a una grande applicazione che aveva cinque contesti per un singolo database. Alla fine, abbiamo finito per rimuovere tutti i contesti tranne uno: tornare a un singolo contesto.

All'inizio l'idea di più contesti sembra una buona idea. Siamo in grado di separare il nostro accesso ai dati in domini e fornire diversi contesti leggeri e puliti. Sembra DDD, vero? Ciò semplificherebbe il nostro accesso ai dati. Un altro argomento è per le prestazioni in quanto accediamo solo al contesto di cui abbiamo bisogno.

Ma in pratica, man mano che la nostra applicazione cresceva, molte delle nostre tabelle condividevano relazioni tra i nostri vari contesti. Ad esempio, le query alla tabella A nel contesto 1 richiedono anche l'unione della tabella B nel contesto 2.

Questo ci ha lasciato con un paio di scelte sbagliate. Potremmo duplicare le tabelle nei vari contesti. Ci abbiamo provato. Ciò ha creato diversi problemi di mappatura, incluso un vincolo EF che richiede che ogni entità abbia un nome univoco. Quindi abbiamo finito con entità denominate Person1 e Person2 nei diversi contesti. Si potrebbe sostenere che questo sia stato un design scadente da parte nostra, ma nonostante i nostri migliori sforzi, è così che la nostra applicazione è cresciuta nel mondo reale.

Abbiamo anche provato a interrogare entrambi i contesti per ottenere i dati di cui avevamo bisogno. Ad esempio, la nostra logica aziendale richiederebbe la metà di ciò di cui aveva bisogno dal contesto 1 e l'altra metà dal contesto 2. Ciò presentava alcuni problemi importanti. Invece di eseguire una query su un singolo contesto, abbiamo dovuto eseguire più query in contesti diversi. Questo ha una vera penalità prestazionale.

Alla fine, la buona notizia è che è stato facile eliminare i molteplici contesti. Il contesto deve essere un oggetto leggero. Quindi non penso che le prestazioni siano un buon argomento per più contesti. In quasi tutti i casi, ritengo che un singolo contesto sia più semplice, meno complesso e probabilmente funzionerà meglio e non sarà necessario implementare una serie di soluzioni alternative per farlo funzionare.

Ho pensato a una situazione in cui più contesti potrebbero essere utili. È possibile utilizzare un contesto separato per risolvere un problema fisico con il database in cui contiene effettivamente più di un dominio. Idealmente, un contesto sarebbe one-to-one per un dominio, che sarebbe one-to-one per un database. In altre parole, se un insieme di tabelle non è in alcun modo correlato alle altre tabelle in un determinato database, è probabile che debbano essere estratte in un database separato. Mi rendo conto che non è sempre pratico. Ma se un set di tabelle è così diverso che ti sentiresti a tuo agio nel separarle in un database separato (ma scegli di non farlo) allora potrei vedere il caso di usare un contesto separato, ma solo perché in realtà ci sono due domini separati.

Per quanto riguarda i micro-servizi, un singolo contesto ha ancora senso. Tuttavia, per i micro-servizi, ogni servizio avrebbe il proprio contesto che include solo le tabelle del database relative a quel servizio. In altre parole, se il servizio x accede alle tabelle 1 e 2 e il servizio y accede alle tabelle 3 e 4, ogni servizio avrebbe il proprio contesto univoco che include tabelle specifiche per quel servizio.

Sono interessato ai tuoi pensieri.


8
Devo essere d'accordo qui, in particolare quando si prende di mira un database esistente. Sto lavorando a questo problema in questo momento, e la mia sensazione finora è: 1. Avere la stessa tabella fisica in più contesti è una cattiva idea. 2. Se non riusciamo a decidere che una tabella appartenga a un contesto o a un altro, i due contesti non sono abbastanza distinti per essere logicamente separati.
jkerak,

3
Direi che, quando fai CQRS, non avresti alcuna relazione tra contesti (ogni vista potrebbe avere il suo contesto) quindi questo avvertimento non si applica a tutti i casi in cui si potrebbe desiderare di avere più contesti. Invece di unire e fare riferimento, utilizzare la duplicazione dei dati per ciascun contesto. - Questo non nega l'utilità di questa risposta però :)
urbanhusky

1
Ho sentito il dolore che hai affrontato in profondità! : / Penso anche che un contesto sia la scelta migliore per la semplicità.
Ahmet,

1
La mia unica argomentazione contro, osservando che altrimenti sono pienamente d'accordo, riguarda l'identità. Soprattutto con il ridimensionamento orizzontale, il livello di identità deve essere separato in quasi tutti i casi in cui viene introdotto il bilanciamento del carico. Almeno, è quello che sto trovando.
Barry,

5
Per me sembra che tu non sia andato fino in fondo DDD, se i tuoi aggregati avessero bisogno di conoscere altri aggregati. Se devi fare riferimento a qualcosa, ci sono due ragioni per cui: sono nello stesso aggregato, il che significa che devono essere cambiati nella stessa transazione o non lo sono e hai sbagliato i tuoi confini.
Simons0n,

54

Questo thread è appena uscito su StackOverflow e quindi volevo offrire un'altra "B) certezza che andrà tutto bene" :)

Sto facendo esattamente questo tramite il modello di contesto limitato DDD. Ne ho scritto nel mio libro, Programming Entity Framework: DbContext ed è al centro di un modulo di 50 minuti all'interno di uno dei miei corsi su Pluralsight -> http://pluralsight.com/training/Courses/TableOfContents/efarchitecture


7
Il video di formazione su Pluralsight è stato molto buono nello spiegare i grandi concetti, tuttavia, gli esempi forniti sono troppo banali rispetto a una soluzione aziendale (dove ad esempio NuGet di assiemi con definizioni DbContext o assiemi modulari caricano dinamicamente). DDD Bounded Context è stato completamente interrotto dal tuo esempio finale in cui è stato definito un DbContext duplicato per contenere dichiarazioni duplicate per ciascun DbSet. Apprezzo che tu sia limitato dalla tecnologia. Mi piacciono molto i tuoi video, ma questo mi ha lasciato desiderare di più.
Victor Romeo,

5
Stavo sicuramente mirando a una visione d'insieme. i problemi relativi ai pacchetti nuget nelle app di grandi dimensioni sono piuttosto fuori contesto per un video ef. Ri "esempi" rotti ... Eh? Forse è meglio portarlo al convo privato poiché una critica del mio corso è piuttosto fuori portata (e forse inappropriata) per questo forum. Penso che SO ti permetta di contattarmi direttamente.
Julie Lerman,

57
Sarebbe stato bello avere Julie a condividere alcune informazioni sul problema / domanda del PO. Invece la posta è solo per promuovere un abbonamento a pagamento al pluralinsight. Se una spina per un prodotto, almeno un collegamento alle informazioni sulla soluzione suggerita (DDD Bounded context Pattern) sarebbe utile. 'DDD' è ciò che è descritto a p.222 di "Programming Entity Framework: DBContext"? Perché ho cercato (nessun indice) "DDD" o "Contesto limitato" e non riesco a individuare ... Non vedo l'ora che tu faccia nuove revisioni per EF6 ...
Narcissist compassionevole

12
scusate, non stavo cercando di promuovere, ma solo di aggiungere all'OP "volere rassicurazioni". Ladislav e altri hanno fatto un ottimo lavoro con i dettagli. Quindi stavo solo provando a qualcosa che ho già creato che va molto più in profondità di quanto potrei eventualmente trasmettere su SO. Qui ci sono altre risorse in cui ho trattato alcuni dei approfondita roba: msdn.microsoft.com/en-us/magazine/jj883952.aspx & msdn.microsoft.com/en-us/magazine/dn342868.aspx & oredev.org / 2013 / mer-ven-conference / ...
Julie Lerman,

sintesi dei suggerimenti codice @JulieLerman 's vedere la mia risposta stackoverflow.com/a/36789520/1586498
OzBob

46

Distinguere i contesti impostando lo schema predefinito

In EF6 puoi avere più contesti, basta specificare il nome per lo schema del database predefinito nel OnModelCreatingmetodo della tua DbContextclasse derivata (dove si trova la configurazione Fluent-API). Questo funzionerà in EF6:

public partial class CustomerModel : DbContext
{   
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema("Customer");

        // Fluent API configuration
    }   
}

In questo esempio verrà utilizzato "Cliente" come prefisso per le tabelle del database (anziché "dbo"). Ancora più importante, prefigura anche le __MigrationHistorytabelle, ad es Customer.__MigrationHistory. Quindi puoi avere più di una __MigrationHistorytabella in un singolo database, una per ogni contesto. Quindi le modifiche apportate per un contesto non interferiranno con l'altro.

Quando si aggiunge la migrazione, specificare il nome completo della classe di configurazione (derivata da DbMigrationsConfiguration) come parametro nel add-migrationcomando:

add-migration NAME_OF_MIGRATION -ConfigurationTypeName FULLY_QUALIFIED_NAME_OF_CONFIGURATION_CLASS


Una breve parola sul tasto di scelta rapida

Secondo questo articolo MSDN " Capitolo - Modelli multipli destinati allo stesso database " EF 6 probabilmente gestirà la situazione anche se MigrationHistoryesistesse solo una tabella, poiché nella tabella è presente una colonna ContextKey per distinguere le migrazioni.

Tuttavia preferisco avere più di una MigrationHistorytabella specificando lo schema predefinito come spiegato sopra.


Utilizzo di cartelle di migrazione separate

In un tale scenario, potresti anche voler lavorare con diverse cartelle "Migrazione" nel tuo progetto. È possibile impostare la DbMigrationsConfigurationclasse derivata di conseguenza utilizzando la MigrationsDirectoryproprietà:

internal sealed class ConfigurationA : DbMigrationsConfiguration<ModelA>
{
    public ConfigurationA()
    {
        AutomaticMigrationsEnabled = false;
        MigrationsDirectory = @"Migrations\ModelA";
    }
}

internal sealed class ConfigurationB : DbMigrationsConfiguration<ModelB>
{
    public ConfigurationB()
    {
        AutomaticMigrationsEnabled = false;
        MigrationsDirectory = @"Migrations\ModelB";
    }
}


Sommario

Tutto sommato, puoi dire che tutto è ben separato: contesti, cartelle di migrazione nel progetto e tabelle nel database.

Sceglierei una soluzione del genere, se ci sono gruppi di entità che fanno parte di un argomento più ampio, ma che non sono collegati (tramite chiavi esterne) tra loro.

Se i gruppi di entità non hanno nulla a che fare l'uno con l'altro, vorrei creare un database separato per ciascuno di essi e accedervi anche in progetti diversi, probabilmente con un unico contesto in ciascun progetto.


Cosa fai quando devi aggiornare 2 entità che si trovano in contesti diversi?
Dal

Vorrei creare una nuova classe (di servizio) che conosca entrambi i contesti, pensare a un buon nome e alle responsabilità di questa classe e fare questo aggiornamento in uno dei suoi metodi.
Martin,

7

Semplice esempio per ottenere quanto segue:

    ApplicationDbContext forumDB = new ApplicationDbContext();
    MonitorDbContext monitor = new MonitorDbContext();

Scorri solo le proprietà nel contesto principale: (usato per creare e mantenere il DB) Nota: usa solo un metodo protetto: (qui l'entità non è esposta)

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext()
        : base("QAForum", throwIfV1Schema: false)
    {

    }
    protected DbSet<Diagnostic> Diagnostics { get; set; }
    public DbSet<Forum> Forums { get; set; }
    public DbSet<Post> Posts { get; set; }
    public DbSet<Thread> Threads { get; set; }
    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
    }
}

MonitorContext: esponi qui un'entità separata

public class MonitorDbContext: DbContext
{
    public MonitorDbContext()
        : base("QAForum")
    {

    }
    public DbSet<Diagnostic> Diagnostics { get; set; }
    // add more here
}

Modello diagnostico:

public class Diagnostic
{
    [Key]
    public Guid DiagnosticID { get; set; }
    public string ApplicationName { get; set; }
    public DateTime DiagnosticTime { get; set; }
    public string Data { get; set; }
}

Se lo desideri, puoi contrassegnare tutte le entità come protette all'interno del ApplicationDbContext principale, quindi creare contesti aggiuntivi in ​​base alle necessità per ogni separazione di schemi.

Usano tutti la stessa stringa di connessione, tuttavia usano connessioni separate, quindi non attraversare le transazioni ed essere consapevoli dei problemi di blocco. Generalmente la tua separazione progettuale quindi questo non dovrebbe accadere comunque.


2
Questo ha aiutato molto. Il contesto "secondario" non deve dichiarare la tabella condivisa. Basta aggiungere manualmente la sua DbSet<x>definizione. Lo faccio in una classe parziale corrispondente a ciò che fa EF Designer.
Glen Little,

Mi hai risparmiato un sacco di mal di testa, signore! Hai fornito una soluzione concreta anziché la risposta accettata. Molto apprezzato!
WoIIe

6

Promemoria: se si combinano più contesti, assicurarsi di tagliare e incollare tutte le funzionalità dei vari RealContexts.OnModelCreating()nel singolo CombinedContext.OnModelCreating().

Ho solo perso tempo a cercare perché le mie relazioni di eliminazione in cascata non venivano conservate solo per scoprire che non avevo portato il modelBuilder.Entity<T>()....WillCascadeOnDelete();codice dal mio contesto reale nel mio contesto combinato.


6
Invece di tagliare e incollare, potresti semplicemente chiamare OtherContext.OnModelCreating()dal tuo contesto combinato?
AlexFoxGill,

4

Il mio istinto mi ha detto la stessa cosa quando mi sono imbattuto in questo disegno.

Sto lavorando su una base di codice in cui ci sono tre dbContexts in un database. 2 dei 3 dbcontexts dipendono dalle informazioni di 1 dbcontext perché servono i dati amministrativi. Questo design ha posto dei vincoli su come è possibile eseguire una query sui dati. Ho riscontrato questo problema in cui non è possibile unirsi a dbcontexts. Invece quello che devi fare è interrogare i due dbcontexts separati, quindi eseguire un join in memoria o scorrere entrambi per ottenere la combinazione dei due come set di risultati. Il problema è che invece di eseguire una query per un set di risultati specifico, ora si caricano tutti i record in memoria e si esegue quindi un join con i due set di risultati in memoria. Può davvero rallentare le cose.

Farei la domanda "solo perché puoi, dovresti?"

Vedi questo articolo per il problema che ho riscontrato in relazione a questo progetto. L'espressione LINQ specificata contiene riferimenti a query associate a contesti diversi


3
Ho lavorato su un grande sistema in cui avevamo più contesti. Una delle cose che ho scoperto è che a volte è stato necessario includere lo stesso DbSet in più contesti. Da un lato ciò rompe alcune preoccupazioni sulla purezza, ma ti consente di completare le tue domande. Nel caso in cui vi siano determinate tabelle di amministrazione che è necessario leggere, è possibile aggiungerle a una classe DbContext di base ed ereditarle nei contesti del modulo app. Lo scopo del contesto di amministrazione "reale" potrebbe essere ridefinito come "fornire manutenzione per le tabelle di amministrazione", anziché fornire tutto l'accesso ad esse.
JMarsch,

1
Per quello che vale, sono sempre andato avanti e indietro se ne valesse la pena. Da un lato, con contesti separati, c'è meno da sapere per uno sviluppatore che vuole solo lavorare su un modulo e ti senti più sicuro nel definire e utilizzare le proiezioni personalizzate (perché non sei preoccupato per gli effetti che avrà sugli altri moduli). Dall'altro, si verificano alcuni problemi quando è necessario condividere i dati nel contesto.
JMarsch,

1
Non DEVI includere entità in entrambi, puoi sempre ottenere gli ID ed eseguire una seconda query in un contesto diverso. Per i sistemi di piccole dimensioni questo è negativo, per i DB / sistemi più grandi con molti sviluppatori la coerenza delle strutture multi-tabella è un problema molto più grande e più difficile di 2 query.
user1496062

4

Ispirato da [DDJ MSDN Mag Article 2013 di @JulieLerman] [1]

    public class ShippingContext : BaseContext<ShippingContext>
{
  public DbSet<Shipment> Shipments { get; set; }
  public DbSet<Shipper> Shippers { get; set; }
  public DbSet<OrderShippingDetail> Order { get; set; } //Orders table
  public DbSet<ItemToBeShipped> ItemsToBeShipped { get; set; }
  protected override void OnModelCreating(DbModelBuilder modelBuilder)
  {
    modelBuilder.Ignore<LineItem>();
    modelBuilder.Ignore<Order>();
    modelBuilder.Configurations.Add(new ShippingAddressMap());
  }
}

public class BaseContext<TContext>
  DbContext where TContext : DbContext
{
  static BaseContext()
  {
    Database.SetInitializer<TContext>(null);
  }
  protected BaseContext() : base("DPSalesDatabase")
  {}
}   

"Se stai effettuando un nuovo sviluppo e vuoi consentire a Code First di creare o migrare il tuo database in base alle tue classi, dovrai creare un" modello super "usando un DbContext che includa tutte le classi e le relazioni necessarie per creare un modello completo che rappresenti il ​​database. Tuttavia, questo contesto non deve ereditare da BaseContext. " JL


2

Per prima cosa nel codice, puoi avere più DBContext e un solo database. Devi solo specificare la stringa di connessione nel costruttore.

public class MovieDBContext : DbContext
{
    public MovieDBContext()
        : base("DefaultConnection")
    {

    }
    public DbSet<Movie> Movies { get; set; }
}

Sì, puoi, ma come puoi eseguire query da entità diverse da contesti db diversi?
Reza,

2

Un altro po 'di "saggezza". Ho un database che affronta sia Internet che un'app interna. Ho un contesto per ogni volto. Questo mi aiuta a mantenere una segregazione disciplinata e sicura.


1

Voglio condividere un caso in cui penso che la possibilità di avere più DBContexts nello stesso database abbia un buon senso.

Ho una soluzione con due database. Uno è per i dati di dominio tranne le informazioni dell'utente. L'altro è solo per informazioni sull'utente. Questa divisione è guidata principalmente dal regolamento generale sulla protezione dei dati dell'UE . Avendo due database, posso spostare liberamente i dati del dominio (ad es. Da Azure al mio ambiente di sviluppo) purché i dati dell'utente rimangano in un posto sicuro.

Ora per il database degli utenti ho implementato due schemi tramite EF. Uno è quello predefinito fornito dal framework Identity AspNet. L'altro è il nostro implementare qualsiasi altra cosa relativa all'utente. Preferisco questa soluzione piuttosto che estendere lo schema ApsNet, perché posso facilmente gestire le modifiche future a AspNet Identity e allo stesso tempo la separazione chiarisce ai programmatori che "le nostre informazioni utente" vanno nello schema utente specifico che abbiamo definito .


2
Non riesco a vedere alcuna domanda nella mia risposta. Non sto facendo una sola domanda! Piuttosto condividere uno scenario in cui l'argomento della discussione ha un buon senso.
freilebt,

0

Eh, ho trascorso un bel po 'di tempo su un problema con contesti DB separati per ogni schema DB, spero che possa aiutare qualcun altro ...

Di recente ho iniziato a lavorare su un progetto che aveva un database con 3 schemi (primo approccio DB), uno dei quali per la gestione degli utenti. C'era un contesto DB impalcato da ogni schema separato. Naturalmente, gli utenti erano collegati anche ad altri schemi, ad es. schema KB aveva una tabella Argomento, che aveva "creato da", "modificato da ultimo" ecc. FK nello schema identità, tabella utente.

Questi oggetti sono stati caricati separatamente in C #, in primo luogo, l'argomento è stato caricato da 1 contesto, quindi gli utenti sono stati caricati tramite ID utente dall'altro contesto db - non è carino, è necessario risolvere questo problema! (simile all'utilizzo di più dbcontexts nello stesso database con EF 6 )

Innanzitutto, ho provato ad aggiungere le istruzioni FK mancanti dallo schema di identità allo schema KB, a EF modelBuilder nel contesto del KB KB. Come se ci fosse solo 1 contesto, ma l'ho separato in 2.

modelBuilder.Entity<Topic>(entity =>
{
  entity.HasOne(d => d.Creator)
    .WithMany(p => p.TopicCreator)
    .HasForeignKey(d => d.CreatorId)
    .HasConstraintName("fk_topic_app_users");

Non ha funzionato, poiché il contesto kb db non aveva alcuna informazione sull'oggetto utente, postgres ha restituito un errore relation "AppUsers" does not exist. L'istruzione Select non aveva informazioni adeguate su schema, nomi dei campi ecc.

Ho quasi rinunciato, ma poi ho notato un interruttore "-d" durante l'esecuzione dotnet ef dbcontext scaffold. Abbreviazione di -data-annotations - Usa gli attributi per configurare il modello (dove possibile). Se omesso, viene utilizzata solo l'API fluente. Con questa opzione specificata, le proprietà dell'oggetto non sono state definite nel contesto db OnModelCreating(), ma piuttosto sull'oggetto stesso, con attributi.

In questo modo, EF ha ottenuto informazioni sufficienti per generare un'istruzione SQL corretta con nomi di campi e schemi corretti.

TL; DR: contesti DB separati non gestiscono bene le relazioni (FK) tra loro, ogni contesto ha solo informazioni sulle proprie entità. Quando si specifica l'attivazione "-data-annotations" dotnet ef dbcontext scaffold, queste informazioni non vengono memorizzate in ogni contesto separato, ma sugli oggetti DB stessi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.