Una delle features che sicuramente in molti aspettavano con la nuova release di EF è la possibilità di eseguire il mapping delle entità tramite Stored Procedures. Con la versione 6 di Entity Framework (attualmente in beta 1), abbiamo finalmente questa possibilità, e l'utilizzo è veramente immediato.
Supponiamo di avere un Domain Model simile a quello mostrato nella figura seguente:
Dove abbiamo un insieme di oggetti ereditati, ed una navigation property che collega l'oggeto Car ad una collezione di oggetti Optional.
Aggiungiamo il riferimento ad EF6 utilizzando il gestore di "NuGet Packages" selezionando tra le varie opzioni "Include Prerelease" e poi selezioniamo il package "Entity Framework":
Eseguiamo la configurazione delle classi che compongono il nostro Domain tramite Code First Fluent Api ed aggiungiamo il codice seguente:
modelBuilder.Entity<Domain.Car>().MapToStoredProcedures();
modelBuilder.Entity<Domain.Optional>().MapToStoredProcedures();
Il quale "informa" il run-time di Entity Framework che per le entità "Car" ed "Optional" le operazioni di Insert\Delete\Update verranno utilizzate delle Stored Procedures (che rispondono alle convenzioni richieste da EF).
A questo punto entra in scena EF Migrations ed il "fantastico" comando "Add-Migration", che produce tutto il codice, e non solo, necessario ad aggiornare il database:
public override void Up()
{
CreateStoredProcedure(
"DomusDotNet.Car_Insert",
p => new
{
Brand = p.String(maxLength: 128),
Model = p.String(maxLength: 128),
EngineSize = p.String(maxLength: 128),
HP = p.Int(),
DailyRent = p.Single(),
FreeDailyKm = p.Int(),
Note = p.String(maxLength: 128),
},
body:
@"INSERT [DomusDotNet].[Vehicles]([Brand], [Model], [EngineSize], [HP], [DailyRent], [FreeDailyKm], [Note])
VALUES (@Brand, @Model, @EngineSize, @HP, @DailyRent, @FreeDailyKm, @Note)
DECLARE @Id int
SELECT @Id = [Id]
FROM [DomusDotNet].[Vehicles]
WHERE @@ROWCOUNT > 0 and [Id] = scope_identity()
INSERT [DomusDotNet].[Cars]([Id])
VALUES (@Id)
SELECT t0.[Id]
FROM [DomusDotNet].[Vehicles] as t0
WHERE @@ROWCOUNT > 0 and t0.[Id] = @Id"
);
...
Unico neo, se utilizzate uno schema diverso da "dbo" (anche se avete specificato tramite la nuova API HasDefaultSchema("...") lo schema corretto) bisogna modificare manualmente il nome delle Stored Procedures, altrimenti a run-time verrà generata un'eccezione.
Scriviamo del codice di test tipo:
int rowsAffected = 0;
Domain.Car fordFocus = new Domain.Car()
{
Brand = "FORD",
DailyRent = 45.0f,
EngineSize = "1600",
FreeDailyKm = 100,
HP = 115,
Model = "Focus",
Note = "With Radio and Power Steering."
};
fordFocus.Optionals.Add(new Domain.Accesory() { Description = "Radio Sony", Removable = true, Price = 10 });
fordFocus.Optionals.Add(new Domain.Accesory() { Description = "Power Steering", Removable = false, Price = 0 });
using (Data.CarRentalDB db = new Data.CarRentalDB())
{
db.Cars.Add(fordFocus);
rowsAffected = db.SaveChanges();
}
Utilizzando il nostro amico "Sql Profiler" vediamo come le operazioni di accesso alla tabella per Insert\Update\Delete utilizzano le Stored Procedures generate in precedenza:
Di questo ed altro parleremo in dettaglio durante l'evento EF@Work organizzato da DomusDotNet :-)