Post on 15-Jul-2015
transcript
#sqlsatParma
#sqlsat355November 22nd, 2014
Entity Framework 6 for developers
Code-First
Michael Denny
@dennymic
about.me/micdenny
#sqlsatParma
#sqlsat355November 22nd, 2014
Speaker info
Microsoft Visual C# MVP
Blogs: [ITA] http://blogs.dotnethell.it/regulator/
[ENG] http://dennymichael.net/
Community/Forum: http://www.dotnethell.it
Twitter: @dennymic
More details on: http://about.me/micdenny/
#sqlsatParma
#sqlsat355November 22nd, 2014
Agenda
Concetti ORM
Scegliere tra designer o codice
Entity Framework Code-First in azione
Evolvere il database: Code-First Migrations
Importare un database esistente
Strumenti di Logging e Profiling
What’s Next
Q&A
#sqlsatParma
#sqlsat355November 22nd, 2014
Visual Studio Community 2013
Include tutte le funzionalità di Visual Studio Professional 2013
Sviluppatori indipendenti per applicazioni sia free che a pagamento, studenti,
collaboratori open source, e organizzazioni con team fino a 5 persone
#sqlsatParma
#sqlsat355November 22nd, 2014
Concetti ORM
Integrazione tra OOP e RDBMS
Persistenza dei dati con interfaccia
unificata
Interfaccia orientata agli oggetti
Astrazione sul tipo di database utilizzato
Sviluppo nel vostro linguaggio (C# )
Riduzione di codice ripetitivo CRUD
Codice più «pulito» e ben strutturato
#sqlsatParma
#sqlsat355November 22nd, 2014
Entity Framework
Implementazione ORM open-source per
ADO.NET
Librerie su NuGet
Lazy-Load
Caching (object, query plan, metadata)
Gestione della Concorrenza
Repository Pattern (DbSet)
Unit of Work (DbContext)
http://msdn.microsoft.com/data/ee712907
#sqlsatParma
#sqlsat355November 22nd, 2014
Database
Esistente
Database First• Creazione modello partendo da un
DB esistente (Reverse Engineering)
• Classi auto-generate dal modello
Code First (Database Esistente)• Definizione classi da codice
• Strumenti per l’auto-generazione delle
classi partendo da un DB esistente
(Reverse Engineering)
Nuovo
Database
Model First• Creazione modello da designer
• Database creato dal modello
• Classi auto-generate dal modello
Code First (Nuovo Database)• Definizione classi da codice
• Database creato dal modello
• Possibilità di evolvere il database
utilizzando le Migrations
Designer o Codice
#sqlsatParma
#sqlsat355November 22nd, 2014
EF Code-First in azione
Modellazione dati
Convenzioni
Operazioni basilari (CRUD)
Query LINQ
Personalizzazioni con Data Annotation
Personalizzazioni con Fluent API
#sqlsatParma
#sqlsat355November 22nd, 2014
Modellazione dati
POCO class
public class Blog{
public int BlogId { get; set; }public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; } }
public class Post{
public int PostId { get; set; }public string Title { get; set; }public string Content { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; } }
#sqlsatParma
#sqlsat355November 22nd, 2014
Modellazione dati
Le proprietà di navigazione definiscono il tipo di relazione
tra le entità in base al tipo di ritorno:
Riferimento, per relazioni uno a uno
Collezione, per relazioni uno a molti
Includere sempre anche la foreign
key sulla classe che rappresenta
l’oggetto dipendente
Virtual abilita il Lazy Loading
vedi: http://msdn.microsoft.com/data/jj574232
public class Blog{
public int BlogId { get; set; }public string Name { get; set; }
public virtual ICollection<Post> Posts{ get; set; }
}
public class Post{
public int PostId { get; set; }public string Title { get; set; }public string Content { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; } }
#sqlsatParma
#sqlsat355November 22nd, 2014
DbContext e DbSet
Responsabile dell’interazione con il database
In grado di creare il database dal modello classi
Gestisce e popola gli oggetti entità
Rileva le modifiche (change tracking)
Persiste le informazioni a database
Implementa la Unit of Work (SaveChanges)
Espone proprietà DbSet (Repository Pattern) che
rappresentano collezioni di specifiche entità
public class BloggingContext : DbContext{
public DbSet<Blog> Blogs { get; set; }public DbSet<Post> Posts { get; set; }
}
#sqlsatParma
#sqlsat355November 22nd, 2014
DEMO 01
Modellazione con POCO class
DbContext
DbSet
Creazione database
#sqlsatParma
#sqlsat355November 22nd, 2014
Convenzioni
System.Data.Entity.ModelConfiguration.Conventions
Chiave primaria
Relazioni
Connection string
Rimuovere una convenzione
Convenzioni personalizzate
#sqlsatParma
#sqlsat355November 22nd, 2014
Convenzioni: Chiave Primaria
Nome proprietà "Id" (non case sentitive)
Nome classe seguito da "Id", es.: "UserId"
Identity Column se il tipo è numerico o GUID
public class Blog{
public int BlogId { get; set; }public string Name { get; set; }
public virtual ICollection<Post> Posts { get; set; } }
#sqlsatParma
#sqlsat355November 22nd, 2014
Basate sulle proprietà di navigazione
Foreign key deve avere lo stesso tipo dato della chiave
primaria e nome: 1. «nome proprietà di navigazione»«nome proprietà chiave primaria» es.:
BlogBlogId
2. «nome classe principale»«nome proprietà chiave primaria» es.:
BlogBlogId
3. «nome proprietà chiave primaria»
es.: BlogId
Non è case sensitive
FK nullable = relazione opzionale
Cascade delete se FK != nullable
public class Post{
public int PostId { get; set; }public string Title { get; set; }public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; } }
public class Blog{
public int BlogId { get; set; }}
Convenzioni: Relazioni
#sqlsatParma
#sqlsat355November 22nd, 2014
Convenzioni: Connection String
DbContext() = Code-First + IConnectionFactory:
Nome db: «namespace».«nome classe dbcontext>
Server: SQL Express oppure LocalDb, se entrambi sono
installati, viene utilizzato SQL Express
DbContext("MyDatabase") = Code-First +
IConnectionFactory:
Nome db: MyDatabase
Server: SQL Express o LocalDb
Se nell’app.config esiste una connection string con lo stesso
nome, allora viene usata quella e bypassato il factory
DbContext("name=MyDatabase") = Code-First:
Forza la dichiarazione della connection string su app.config
#sqlsatParma
#sqlsat355November 22nd, 2014
Convenzioni: Personalizzazioni
Rimuovere una convenzione
Convenzioni personalizzate
public class BloggingContext : DbContext{
protected override void OnModelCreating(DbModelBuilder modelBuilder){
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();}
}
public class BloggingContext : DbContext{
protected override void OnModelCreating(DbModelBuilder modelBuilder){
modelBuilder.Properties<int>().Where(t => t.Name.StartsWith("id", StringComparison.InvariantCultureIgnoreCase)).Configure(t => t.IsKey().HasColumnOrder(1));
} }
#sqlsatParma
#sqlsat355November 22nd, 2014
Convenzioni
IdKeyDiscoveryConvention
Rileva le proprietà per la chiave primaria
PluralizingTableNameConvention
Nome tabella = plurale nome classe
PluralizingEntitySetNameConvention
Proprietà di navigazione = plurale nome tipo dato
DecimalPropertyConvention
Setta la precision e la scale (18,2) per le proprietà decimali
Tanti tanti altri...
Vedi: System.Data.Entity.ModelConfiguration.Conventions
#sqlsatParma
#sqlsat355November 22nd, 2014
Operazioni basilari (CRUD)
using (var db = new BloggingContext()) {
var blog = new Blog();blog.Name = "Il mio primo blog";db.Blogs.Add(blog);db.SaveChanges();
}
using (var db = new BloggingContext()) {
var blog = db.Blogs.Find(1);Console.WriteLine(blog.Name);
}
using (var db = new BloggingContext()) {
var blog = db.Blogs.Find(1);blog.Name = "Nome modificato!!";db.SaveChanges();
}
using (var db = new BloggingContext()) {
var blog = db.Blogs.Find(1);db.Blogs.Remove(blog);db.SaveChanges();
}
#sqlsatParma
#sqlsat355November 22nd, 2014
LINQ Query
Lambda Expression
Query LINQ: Filtrare
using (var db = new BloggingContext()) {
var results = from post in db.Postswhere post.Title.Contains("trovami")select post;
foreach (var result in results)Console.WriteLine(result.Title);
}
using (var db = new BloggingContext()) {
var results = db.Posts.Where(post => post.Title.Contains("trovami"));
foreach (var result in results)Console.WriteLine(result.Title);
}
SELECT
[Extent1].[PostId] AS [PostId],
[Extent1].[Title] AS [Title],
[Extent1].[Content] AS [Content],
[Extent1].[BlogId] AS [BlogId]
FROM
[dbo].[Posts] AS [Extent1]
WHERE
[Extent1].[Title] LIKE N'%trovami%'
#sqlsatParma
#sqlsat355November 22nd, 2014
Query LINQ: Ordinare
using (var db = new BloggingContext()) {
var results = from post in db.Postsorderby post.Titleselect post;
foreach (var result in results)Console.WriteLine(result.Title);
}
using (var db = new BloggingContext()) {
var results = db.Posts.OrderBy(t => t.Title);
foreach (var result in results)Console.WriteLine(result.Title);
}
using (var db = new BloggingContext()) {
var results = db.Posts.Where(t => t.Content.Contains("trovami")).OrderBy(t => t.Title);
foreach (var result in results)Console.WriteLine(result.Title);
}
SELECT
[Extent1].[PostId] AS [PostId],
[Extent1].[Title] AS [Title],
[Extent1].[Content] AS [Content],
[Extent1].[BlogId] AS [BlogId]
FROM
[dbo].[Posts] AS [Extent1]
ORDER BY
[Extent1].[Title] ASC
SELECT
[Extent1].[PostId] AS [PostId],
[Extent1].[Title] AS [Title],
[Extent1].[Content] AS [Content],
[Extent1].[BlogId] AS [BlogId]
FROM
[dbo].[Posts] AS [Extent1]
WHERE
[Extent1].[Content] LIKE N'%trovami%'
ORDER BY
[Extent1].[Title] ASC
LINQ Query OrderBy
Lambda Expression OrderBy
Lambda Expression Where + OrderBy
#sqlsatParma
#sqlsat355November 22nd, 2014
Query LINQ: Join(are)
Esplicite
Implicite
using (var db = new BloggingContext()) {
var results = from blog in db.Blogsjoin post in db.Posts on blog.BlogId equals post.BlogIdselect new{
BlogName = blog.Name,PostTitle = post.Title
};foreach (var result in results)
Console.WriteLine("blog: {0} post: {1}", result.BlogName, result.PostTitle); }
using (var db = new BloggingContext()) {
var results = from blog in db.Blogsfrom post in blog.Postsselect new{
BlogName = blog.Name, PostTitle = post.Title
};foreach (var result in results)
Console.WriteLine("blog: {0} post: {1}", result.BlogName, result.PostTitle); }
SELECT
[Extent1].[BlogId] AS [BlogId],
[Extent1].[Name] AS [Name],
[Extent2].[Title] AS [Title]
FROM
[dbo].[Blogs] AS [Extent1]
INNER JOIN
[dbo].[Posts] AS [Extent2]
ON [Extent1].[BlogId] = [Extent2].[BlogId]
#sqlsatParma
#sqlsat355November 22nd, 2014
Query: Stored Procedure
Raw SQL Queries
http://msdn.microsoft.com/data/jj592907public class BloggingContext : DbContext{
public DbSet<Blog> Blogs { get; set; }public DbSet<Post> Posts { get; set; }
public Blog GetBlogById(int blogId){
return this.Blogs.SqlQuery("proc_GetBlogById @p0", blogId).FirstOrDefault();
}
public Blog GetBlogById2(int blogId){
return this.Blogs.SqlQuery("proc_GetBlogById @BlogId",
new SqlParameter("@BlogId", blogId)).FirstOrDefault();
} }
#sqlsatParma
#sqlsat355November 22nd, 2014
Strumenti di Logging
Proprietà Log a delegato
Personalizzazione del contenuto e del formato
Sistema di intercettazione a moduli per
maggiore controllo e flessibilità
Gli Interceptor possono essere aggiunti da
config senza dovere ricompilare il progetto
BloggingContext.Database.Log = Console.Write;
#sqlsatParma
#sqlsat355November 22nd, 2014
DEMO 03
Filtrare
Ordinare
Join(are)
Stored Procedure
Logging
#sqlsatParma
#sqlsat355November 22nd, 2014
Evolvere il database: Code-First Migrations
Sempre sotto il nostro controllo
Personalizzazioni (anche estreme) da
codice
Semplicità di utilizzo
Enable-Migrations
Add-Migration
Update-Database
http://msdn.microsoft.com/data/jj591621
#sqlsatParma
#sqlsat355November 22nd, 2014
DEMO 04
Enable-Migrations
Add-Migrations
Update-Database
Migrate.exe
packages\EntityFramework.6.1.1\tools
#sqlsatParma
#sqlsat355November 22nd, 2014
Importare un database esistente
Da zero senza reverse engineering
Con strumenti di automazione:
Entity Framework Tools (incluso in VS2013)
www.microsoft.com/download/details.aspx?id=40762
Entity Framework Power Tools (NuGet)
EntityFramework Reverse POCO Generator (NuGet)
Code First Migrations anche per database
esistenti/importati
http://msdn.microsoft.com/data/dn579398
#sqlsatParma
#sqlsat355November 22nd, 2014
DEMO 05
Importare un database esistente
Entity Framework Tools
Entity Framework Power Tools
EntityFramework Reverse POCO Generator
#sqlsatParma
#sqlsat355November 22nd, 2014
Strumenti di Profiling
SQL Server Profiler (Microsoft)
SQL XEvent Profiler (free Idera)
EF Profiler (commerciale, installer
HibernatingRhinos.com)
ORM Profiler (commerciale, NuGet)
LINQ Insight Express (free, NuGet)
#sqlsatParma
#sqlsat355November 22nd, 2014
What’s Next
Versione 7 più leggera ed estensibile
Sarà disponibile per nuove piattaforme (Store
App, Phone App, Net Core, Mac, Linux)
Abiliterà nuovi data store, come sistemi non-
relazionali
Il passaggio a EF7 sarà semplificato il più
possibile (DbContext e DbSet saranno presenti)
Sviluppato e disponibile in pre-alpha su GitHub
https://github.com/aspnet/EntityFramework
#sqlsatParma
#sqlsat355November 22nd, 2014
In pillole…
Include(t => t.Posts) evita il lazy loading
http://msdn.microsoft.com/data/jj574232
Find(1) ricerca per chiave
http://msdn.microsoft.com/data/jj573936
Entity<>.ToTable("Posts", "Blog")
per creare Posts nello schema Blog
Async Query & Save
http://msdn.microsoft.com/data/jj819165
CRUD con Stored Procedure
http://msdn.microsoft.com/data/dn468673
#sqlsatParma
#sqlsat355November 22nd, 2014
In pillole…
Considerazioni sulle performance
http://msdn.microsoft.com/data/hh949853
Migliorare lo startup dell’applicazione con NGen
http://msdn.microsoft.com/data/dn582034
Pre-generare le Mapping Views (EDM) per
velocizzare dell’inizializzazione del DbContext
http://msdn.microsoft.com/data/dn469601
Gestione della concorrenza ottimistica
http://msdn.microsoft.com/data/jj592904
#sqlsatParma
#sqlsat355November 22nd, 2014
In pillole…
Migrate.exe per controllare e automatizzare il
processo di migrazione del database
http://msdn.microsoft.com/data/jj618307
AsNoTracking()
http://msdn.microsoft.com/data/jj556203
Disabilitare il tracking automatico
http://msdn.microsoft.com/data/jj556205
Unit Test: testare isolandosi dal database
http://msdn.microsoft.com/data/dn314429