E’ da un po’ di tempo che avrei voluto scrivere questo post, ma tra una cosa e l’altra…meglio tardi che mai. Una delle novità di .NET 4.0 che utilizzo (quasi) quotidianamente è sicuramente EF 4.0 (a dire il vero anche la versione precedente), ma il supporto POCO è tutta un’altra storia. Tra le altre novità , sicuramente MEF (Managed Extensibility Framework) che affianca MAF (Manager Add-In Framework) presente dalla versione 3.5 di .NET. Per chi volesse approfondire questi concetti ricordo dei post di Federico in merito. Veniamo a noi, il titolo, è “un’equazione” il cui risultato vuole rappresentare uno dei scenari seguenti: un’applicazione che deve poter utilizzare due RDBMS diversi per eseguire lo storage delle informazioni (ad esempio come server principale Microsoft SQL Server e come secondario MySQL o viceversa), un’applicazione che deve eseguire una scrittura (contemporanea) su database diversi, un’applicazione che deve scegliere quale database utilizzare secondo delle condizioni iniziali (configurazione) o ancora un’applicazione che a “caldo” possa “selezionare” quale base di dati utilizzare. Sicuramente possono essere considerati molti altri scenari, ma spero di aver reso l’idea della problematica. Per ogni scenario, possono esistere numerose soluzioni, secondo della tecnologia impiegata. Nel post, prendo in considerazione i due framework precedentemente menzionati e assemblati per creare un’applicazione che esegue una serie di INSERT, UPDATE e DELETE (via Entity Framework ovviamente) utilizzando una connessione ad un database Microsoft SQL Server o una connessione ad un database MySQL, utilizzando il supporto POCO. Il Layer Diagrams sottostante rappresenta la dipendenza tra i tre layers (e relativi progetti) presenti della soluzione VS 2010 allegata:
Il Data Layer è composto da due progetti tra loro indipendenti: DBCompany.Storage.MySQL e DBCompany.Storage.SQLServer il cui Entity Data Model contiene due entità Employee e Role ed un’associazione molti a molti:
In ognuno dei progetti è presente il codice seguente (nell’esempio la versione MySQL):
1: [Export(typeof(IDBCompanyEntities))]
2: public partial class MySQLDataConnectorEntities : ObjectContext, IDBCompanyEntities
3: {
4: public const string ConnectionString = "name=MySQLDataConnectorEntities";
5: public const string ContainerName = "MySQLDataConnectorEntities";
6:
7: #region Constructors
8:
9: public MySQLDataConnectorEntities()
10: : base(ConnectionString, ContainerName)
11: {
12: this.ContextOptions.LazyLoadingEnabled = true;
13: }
14:
15: public MySQLDataConnectorEntities(string connectionString)
16: : base(connectionString, ContainerName)
17: {
18: this.ContextOptions.LazyLoadingEnabled = true;
19: }
20:
21: public MySQLDataConnectorEntities(EntityConnection connection)
22: : base(connection, ContainerName)
23: {
24: this.ContextOptions.LazyLoadingEnabled = true;
25: }
26:
27: #endregion
28:
29: public string StorageVersion()
30: {
31: return "MYSQL";
32: }
33:
34: #region ObjectSet Properties
35:
36: public ObjectSet<Employee> Employees
37: {
38: get { return _employees ?? (_employees = CreateObjectSet<Employee>("Employees")); }
39: }
40: private ObjectSet<Employee> _employees;
41:
42: public ObjectSet<Role> Roles
43: {
44: get { return _roles ?? (_roles = CreateObjectSet<Role>("Roles")); }
45: }
46: private ObjectSet<Role> _roles;
47:
48: #endregion
49: }
Che concretizza l’ObjectContext specializzato, il quale a sua volta implementa l’interfaccia IDBCompanyEntities “necessaria” per avere il supporto MEF:
1: public interface IDBCompanyEntities
2: {
3: ObjectSet<Employee> Employees { get; }
4: ObjectSet<Role> Roles { get; }
5: int SaveChanges();
6: int SaveChanges(SaveOptions options);
7: string StorageVersion();
8: }
Il progetto DBCompany.BusinessObjects contiene oltre alla suddetta interfaccia (la quale potrebbe essere inserita in un progetto contenente solo i Contracts) anche la definizione degli oggetti POCO:
Il progetto di test è un’applicazione console che non ha nessun riferimento al Data Layer, ma solo al Domain Layer. In esecuzione si presenta in questo modo:
Prima di procedere “chiede” se si vuole utilizzare il data provider per Microsoft SQL Server o MySQL e secondo che venga premuto il tasto “S” (SQL Server) o un altro tasto, parte una piccola demo che esegue delle scritture sul database scelto. Nella figura sottostante un esempio di esecuzione con il server MySQL:
Inizialmente ho utilizzato come data provider per MySQL la versione trial di DotConnect per MySQL , ma nella versione allegata ho utilizzato la versione 6.3.3(Beta) del data provider fornito direttamente dal sito di MySQL (occorre la registrazione per effettuare il download). In questo contesto MEF è “configurato” per caricare tutti gli assembly presenti nella directory specificata nel codice e permettere all’utente (in questo caso manualmente) quale data provider utilizzare, ma potrebbe essere configurato per considerare un solo add-in per volta. Una critica può essere sollevata dal fatto di dover mantenere aggiornati due Entity Data Model (praticamente) uguali, mentre se ne potrebbe utilizzare solo uno, utilizzando una terzo progetto libreria. Lo scopo del post però non è quello di voler rappresentare un’ottimizzazione algoritmica, ma quella di voler proporre una soluzione ad un problema più o meno comune utilizzando gli ultimi strumenti messi a disposizione dal .Net Framework 4.0.
Nel codice allegato sono presenti gli script per la creazione dei due database di test (ovviamente è necessario che le stringhe di connessione siano configurare in modo opportune).