PDA

Visualizza la versione completa : [VB.NET] Interagire con più database usando un unico strumento


lucisetti
12-04-2012, 00:56
Buonasera a tutti.

Cercando su google non sono riuscita a trovare dei suggerimenti su come poter creare una nuova applicazione in VB.NET che sia in grado di interagire con un numero limitato di DB (MS Sql Server, Oracle, MySQL e MS Access).

In particolare vorrei capire come utilizzare i vari dialetti SQL con un unico strumento.
Ciò al fine di poter offrire un applicativo in grado di lavorare con uno dei 4 DB sopra indicati a scelta del Cliente finale.

Spero di essere stata chiara nell'esposizione.

Grazie

Lucia

rsdpzed
12-04-2012, 08:00
No. non esiste un unico strumento per tutto. Ma lavorandoci su poui raggiungere il risultato che vuoi.

non ti voglio tediare con concetti come il repository pattern, mef, architetture n-layer ecc... facciamo una cosa "fatta a mano" poi se vorrai approfondire, le cose verranno da se.

Progetto entità:
Per prima cosa ti serve un progetto che contiene le classi che rappresentano lo schema del tuo db. Le classi ovviamente non devono essere pari pari allo schema ma possono contenere anche proprietà calcolate e/o metodi nterni (es eta calcolata in base alla data di nascita).


public class Persona
{
//queste corrispondono ai campi della tabella persona nei db
public int ID {get;set;}
public string Nome {get;set;}
public string Cognome {get;set;}
public DateTime DataDiNascita {get;set;}

//arricchisco la classe con prop calcolate se serve
public int età { get { if(....vla bla bla.... return bla bla...} }
}


progetto dal (data access layer):
devi separare completamente la logica di accesso al db dall'implementazione del programma mettendola in un progetto separato (dal). In questo progetto ti crei una cartella (namespace) per ognuno dei db che vuoi supportare e in ognuna una classe che contiene tutti metodi di accesso al db. Questi metodi accettano parametri clr e restituiscono oggetti o collezioni di oggetti entità (quelli del progetto precedente). Internamente fanno le query al db.
Queste 4 classi oviamente avranno tutte gli stessi metodi perciò fuori dalle 4 cartelle ti crei una interfaccia che descrive questi metodi e la fai implementare alle classi.


public class MySqlDalManager : IDalManager
{
//costrittore... connesione al db....

//Inizio ad implemetare i metodi definiti nell'interfaccia...

//cerca tutte le persone che hanno un nome specificato in input
public List<Persona> ElencoPersonePerNome(string nome)
{
//implementazione in mysql
}
...
...

//magari anche un dispose...
}



programma.
Il programma, che sarà un altro progetto, utilizzerà una delle 4 classi del progetto precedente le quali espongono tutti i metodi attraverso l'interfaccia IDalMAnager. MA quale delle 4 implementazioni dovra usare? Utilizzando i late binding e la possibilità di leggere una configurazione dal file app.settings potremmo fare una cosa tipo:


public class Window1
{
//qui dichiaro la classe d accesso ai dati come interfaccia...
private IDalManager dal;

public void Initialize()
{
//...e a runtime sceglo un implementazione in base ad una variabile nel file di configurazione
//leggo da appSettings quale implementazione è configurata per questa istanza...
string implementazione = ConfigurationManager.AppSettings["dal"].ToString();
//...e la implemento (questo è il late binding)
switch (implementazione)
{
case "mySql" : dal = new MySqlDalManager();
case "oracle" : dal = new OracleDalManager();
ecc.. ecc.
}
}
}


cosi puoi usare la classe dalManager sempre allo stesso modo (è esposta solo l'interfaccia) ma solo a runtime si deciderà quale delle 4 implementazioni usare.

Talion
12-04-2012, 09:21
Potresti provare a guardare l'entity framework che ti permette di renderti indipendente dal dbms utilizzato disaccoppiando applicazione e accesso ai dati. In caso di cambio di dbms basterà rieffettuare solo il mapping del modello concettuale senza stravolgere niente

Per accedere ai dati potresti evitare di scrivere query sql ed usare linq to entities evitando cosi di adattare la sintassi sql al specifico dbms.

alka
12-04-2012, 10:17
In questi casi, come è già stato suggerito, si ricorre all'uso di un tool ORM (http://it.wikipedia.org/wiki/Object-relational_mapping), cioè a uno strumento che consenta di modellare il dominio dell'applicazione utilizzando delle classi e disaccoppiando la loro logica dallo "storage" in cui vengono memorizzati (il DB), configurandolo separatamente in modo da poter scegliere solo in seguito quale tipo di storage, ovvero quale formato di database utilizzare.

In ambito Microsoft .NET, Entity Framework è senz'altro una buona soluzione al giorno d'oggi.

Qui (http://www.microsoft.com/italy/beit/Msdn.aspx?video=cfc42534-1672-44bb-ba7d-a0547564f805) c'è un interessante video introduttivo, sia sul discorso generale degli ORM che - nello specifico - su Entity Framework.

rsdpzed
12-04-2012, 11:19
scusate la pignoleria :fagiano: ma non confondiamo le acque (avevo tralasciato gli orm apposta dal mio reply)

Ef è un ORM che fa da ponte tra il db e il modello a oggetti (piu altre cose carine come le join trasparenti, il pattern transaction, la gestione della concorrenza...). MA il file edmx del modello di ef è strettamente legato al rdbms specifico. Non basta cambiare la stringa di connessione.
Infatti ogni compagnia a sua discrezione e con tempi di aggiornamento variabili, si occupa di fornire il plugin per poter utilizzare EF (e altre tecnologie) con il proprio rdbms.

Detto questo, per rispondere alla domanda del topic (applicazione che supporta piu rdbms), nel caso di ef la situazione si complica perche EF oltre all'accesso ai dati ingloba anche il modello (quello che nel mio esempio "rustico" ho chiamato progetto entità separandolo dal dal) per cui l'unico modo per supportare piu rdbms in modo trasparente è quello di switchare le dll (una per ogni implementazione) a runtime con reflection (sempre late binding ma piu "scomodo"), o piu facilmente una tantum in fase di installazione (l'installer sceglie la dll), oppure usare strumenti come mef (mai usati ma dovrebbero servire allo scopo). Personalmente sono soluzioni che non ho mai provato per cui non saprei dire quale sia piu " facile o difficile" rispetto alle altre.

alka
12-04-2012, 11:33
Originariamente inviato da rsdpzed
scusate la pignoleria :fagiano: ma non confondiamo le acque (avevo tralasciato gli orm apposta dal mio reply)

La pignoleria va benissimo, ma qui nessuno ha "confuso le acque": si è risposto alla domanda.


Originariamente inviato da rsdpzed
Ef è un ORM che fa da ponte tra il db e il modello a oggetti (piu altre cose carine come le join trasparenti, il pattern transaction, la gestione della concorrenza...). MA il file edmx del modello di ef è strettamente legato al rdbms specifico. Non basta cambiare la stringa di connessione. Infatti ogni compagnia a sua discrezione e con tempi di aggiornamento variabili, si occupa di fornire il plugin per poter utilizzare EF (e altre tecnologie) con il proprio rdbms.

Ok, e quindi?

Il fatto di poter sostituire il "mapping" tra i propri oggetti e il layer di persistenza è uno strumento che consente di rimanere indipendenti dallo storage finale, esattamente come richiesto. Poi non è ovviamente obbligatorio usare un ORM: ci si può costruire un layer personalizzato così come hai descritto tu.

Il vantaggio offerto da EF - tramite un file di configurazione (EDMX) e un sistema di classi già disponibili - è lo stesso che proponi tu con la tua struttura a interfacce.


Originariamente inviato da rsdpzed
Detto questo, per rispondere alla domanda del topic (applicazione che supporta piu rdbms), nel caso di ef la situazione si complica perche EF oltre all'accesso ai dati ingloba anche il modello (quello che nel mio esempio "rustico" ho chiamato progetto entità separandolo dal dal) per cui l'unico modo per supportare piu rdbms in modo trasparente è quello di switchare le dll (una per ogni implementazione) a runtime con reflection (sempre late binding ma piu "scomodo"), o piu facilmente una tantum in fase di installazione (l'installer sceglie la dll), oppure usare strumenti come mef (mai usati ma dovrebbero servire allo scopo).
Ripeto la domanda di prima: e allora?

E' un tool che, come qualsiasi altro strumento, va approfondito per capirne le potenzialità, ma tecnicamente fa esattamente ciò che è stato richiesto, e assolve allo stesso compito che hai indicato tu con le classi che andresti a scrivere manualmente.


Originariamente inviato da rsdpzed
Personalmente sono soluzioni che non ho mai provato per cui non saprei dire quale sia piu " facile o difficile" rispetto alle altre.
Questo dipende dal tipo di applicazione. Se si tratta di un requisito necessario per un'applicazione che scrive su una sola tabella, allora non mi pare il caso di scomodare un ORM per questo; se l'applicazione è un po' più complessa e di respiro più ampio, francamente non avrei dubbi nell'accantonare la realizzazione a mano di un "data layer" personalizzato al posto di scegliere una soluzione collaudata e documentata che fa già egregiamente quello che mi serve. Ma questo è solo un parere personale.

Talion
12-04-2012, 12:37
Condordo con alka per la pignoleria, anzi a volte è meglio essere pignoli :) , d'altronde confrontarsi dovrebbe aiutare a crescere, quindi ben venga la pignoleria a mio avviso


Ritornando al tema con EF 4 In realtà la cosa è molto semplice, senza andarsi ad ingarbugliare con la reflection.

E' sufficiente in fase di creazione specificare il file ssdl relativo al provider che vuole essere utilizzato. Questo implica il creare il file ssdl relativo al provider che si vuole utilizzare.

Ad esempio per accedere a sql server e mysql 5:

app.config: definizione della stringa di connessione specificando l'ssdl di riferimento




<connectionStrings>
<add name="SqlServer" connectionString="metadata=Northwind.csdl|Northwind.ssdl|Northwind.m sl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=XXXXX\sqlexpress;Initial Catalog=Northwind;Integrated Security=True;&quot;" providerName="System.Data.EntityClient" />

<add name="MySQL" connectionString="metadata=Northwind.csdl|Northwind.MySql.ssdl|North wind.msl;provider=MySql.Data.MySqlClient;provider connection string=&quot;server=localhost;user id=root;password=*******;persist security info=True;database=northwind&quot;" providerName="System.Data.EntityClient" />
</connectionStrings>



-nel file ssdl di MySql ci sarà la definizione dello schema e il relativo provider da usare




<Schema Namespace="NorthwindModel.Store" Alias="Self"
Provider="MySql.Data.MySqlClient"
ProviderManifestToken="MySQL5"
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"
xmlns="http://schemas.microsoft.com/ado/2006/04/edm/ssdl">

.... SCHEMA ...





A questo punto accedere ai dati diventa pressoche uguale sia per mysql che sql server infatti basterà utilizzare la string connection relativa al dbms che si vuole utilizzare.




string connectionString = STRINGA DI CONNESSIONE da app.config;

//Get del context del modello specificato nell'edmx
using (var context = new NorthwindEntities(connectionString))
{

QUERY (linqToEntities)
....


}

rsdpzed
12-04-2012, 12:43
No non volevo dire che non va usato ef anzi. Io lo uso e se dovessi trovarmi a dover fare un applicazione multi rdbms io continuerei ad usarlo.

Forse sono io che ho mal'interpetato la domanda iniziale.
Se vuoi programmare senza preoccuparti del database sottostante ef è ovviamente la soluzione

:)

edit:
ho letto ora il post di talion: un ssdl per ogni modello... effettivamente è piu semplice del previsto :mame:

Talion
12-04-2012, 12:56
Qualche aggiustamento all'ssdl c'è comunque da fare ci sono delle differenze tra mysql e sql server oltre il cambiamento del provider (nell'unico progetto che ho fatto ho usato solo questi due DB per oracle e access non so cosa bisogna toccare :D )

studiofab
06-09-2012, 17:21
buongiorno

riprendo questa discussione, per me di assoluto e alto livello. :mem:

Poiché mi ponevo lo stesso problema (2 db: SQLite e Access), iniziando a studiare un po' i delegate mi chiedevo se in qualche modo si poteva reinidirizzare un metodo verso l'una o l'altra connessione. Lo scopo è quello di poter saltare da un db all'altro, senza modificare i metodi all'interno del programma (le chiamate a query sono molto numerose e per questioni di test sarebbe lunghissimo riscrivere tutto o addirittura raddoppiare le sub!)

Rimane, credo, il problema iniziale: a quale oggetto (classe) generico si può assegnare il relativo oggetto di ciascun database (uguale per questioni di logica, ma diverso nel senso di Tipo)?

Da:
SQLite -> SQLiteCommand, SQLiteDataReader ecc.
OLEDB -> OLEDBCommand, OLEDBDataReader, Adapter, ecc.

arrivare ad un qualcosa di simile ad un vecchio ADO.Recordset su cui riversare una query indipendentemente dalla source.

Grazie e complimenti

Loading