posts - 315, comments - 268, trackbacks - 15

My Links

News

View Pietro Libro's profile on LinkedIn

DomusDotNet
   DomusDotNet

Pietro Libro

Tag Cloud

Article Categories

Archives

Post Categories

Blogs amici

Links

EF CTP 5: Episode 1 :-)

Finalmente sono riuscito a trovare del tempo per iniziare a “studiare” le funzionalità introdotte con la CTP 5 di Entity Framework. Lo scopo del post, e spero altri quanto prima,  è di condividerne lo studio, magari viene fuori qualche discussione interessante sui pro e contro. Fino ad oggi ho sempre definito il modello dei dati utilizzando l’Entity Model Designer di Visual Studio, senza mai utilizzare un approccio di tipo Code First. Nel post utilizzeremo questo tecnica per creare un nuovo schema di database senza utilizzare nessun tipo di attributo di mapping. Partiamo con il creare un nuovo progetto di tipo console (C#) ; In un file di codice “buttiamo giù” qualche classe:

    public class Developer
    {
        public Developer()
        {
            this.Skills = new List<Skill>(3);
            this.Projects = new List<Project>(1);
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public string Surname { get; set; }
        public ICollection<Skill> Skills { get; set; }
        public ICollection<Project> Projects { get; set; }
    }

    public class Project
    {
        public Project()
        {
            Developers = new List<Developer>(1);
        }
        public int Id { get; set; }
        public string Description { get; set; }
        public ICollection<Developer> Developers { get; set; }
    }

    public class Skill
    {
        public int Id { get; set; }
        public string Description { get; set; }        
        public byte Rank { get; set; }
    }

Il nostro modello dati  (no Domain Model, vedi commenti) è costituito da una classe Developer che ha uno o più Skill. Ogni Developer è assegnato ad uno o più Project: la classe Developer espone due ICollection<T> , una di tipo Skill l’altra Project. A sua volta la classe Project espone una collezione ICollection<Developer> contenente tutti gli sviluppatori assegnati ad un progetto. La classe Skill rappresenta le competenze di uno sviluppatore. Il modello sicuramente non è realistico, ma  utile per i nostri esempi.

Per eseguire l’accesso ai dati abbiamo bisogno di definire una classe che derivi da DbContext e che esponga per ogni classe del modello una proprietà DbSet<T>:

    public class Db : DbContext
    {
        public DbSet<Developer> Developers { get; set; }
        public DbSet<Project> Projects { get; set; }
        public DbSet<Skill> Skills { get; set; }
    }

Specifichiamo la stringa di connessione da utilizzare in un file di configurazione (altrimenti verrebbe utilizzata l’istanza di default SQLEXPRESS):

<add name="Db" connectionString="Data Source=server_instance;Initial Catalog=CodeFirstDb;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.Client" />

Prima di continuare  è necessario aggiungere un riferimento alla libreria EntityFramework.dll presente nella directory di installazione della CTP 5 di Entity Framework e System.Data.Entity.dll.

Iniziamo a scrivere il codice del Main:

DbDatabase.DefaultConnectionFactory = new System.Data.Entity.Database.SqlConnectionFactory(ConfigurationManager.ConnectionStrings["Db"].ConnectionString);

Definiamo la strategia di inizializzazione del database:

1) Se desideriamo che  il database sia creato nel cui non sia presente scriviamo:

DbDatabase.SetInitializer<Db>(new CreateDatabaseIfNotExists<Db>());

2) Se vogliamo “droppare” e ricreare il database ogni qual volta il modello dei dati subisca delle modifiche scriviamo:

DbDatabase.SetInitializer<Db>(new DropCreateDatabaseIfModelChanges<Db>());

Utilizzeremo  questo secondo approccio. Scriviamo del codice che ci restituisca il numero di sviluppatori registrati (ovviamente la prima esecuzione restituirà zero):

using (Db db = new Db())
{                
    int developers = (from dev in db.Developer select dev).Count ();
    Console.WriteLine  ("Developer in db: {0}", developers );
}

Prima di avviare l’applicazione, avviamo il profiler di SQL Server per analizzare cosa accade dietro le quinte:

image

Dall’immagine precedente si evince come inizialmente sia verificata l’esistenza del database “CodeFirstDb”. Partendo da zero,  vengono poi eseguiti i diversi  SQL Statement per creare  il database, le tabelle, relazioni, chiave primarie e quanto definito nel modello. Dopodiché viene eseguita la SELECT definita prima nel codice:

image

Ora proviamo a scrivere del codice per aggiungere qualche dato nelle tabelle:

using (Db db = new Db())
{
    int developers = 0;
    int projects = 0;
    developers = (from d in db.Developers select d).Count();
    Console.WriteLine("Developers in db: {0}", developers);

    ////Add a new Developer
    Developer dev1 = new Developer()
    {
        Id = 1,
        Name = "Pinco",
        Surname = "Pallo",
        Skills = new Skill[]{
                new Skill (){ Description ="C# 4.0", Rank=4},
                new Skill (){ Description="LINQ", Rank =4},
                new Skill (){ Description="Entity Framework" ,Rank=4}
            }
    };

    ////Add a new Project
    Project prj = new Project() { Description = "A new e-commerce web site." };

    ////Assign developers at a project.
    prj.Developers.Add(dev1);
    dev1.Projects.Add(prj);

    db.Developers.Add(dev1);                

    ////Save data.
    int rowsAffected = db.SaveChanges();

    developers = (from d in db.Developers select d).Count();
    projects = (from p in db.People select p).Count();

    Console.WriteLine("Projects in db: {0}", developers);
    Console.WriteLine("Developers in db: {0}", developers);
}

Eseguendo l’applicazione otteniamo:

image

Diamo uno sguardo al database creato:

image

Notiamo la tabella di associazione tra Developers e Projects oltre alla tabella EdmMetadata utilizzata da EF per eseguire il Check del modello di database corrente.

Diverse strategie…

Come utilizzare una diversa strategia di inizializzazione del database ? E’ sufficiente creare una classe che implementi l’interfaccia IDatabaseInitializer<T> come ad esempio:

public class DBCustomInitializerStrategy<T> : IDatabaseInitializer<T> where T : Db
{
    #region IDatabaseInitializer<Db> Members

    public void InitializeDatabase(T context)
    {
        if (!context.Database.Exists())
        {
            context.Database.CreateIfNotExists();

            Assembly currentAssembly = Assembly.GetExecutingAssembly();
            StreamReader sr = new StreamReader(currentAssembly.GetManifestResourceStream(
                string.Format("{0}.Install.sql", currentAssembly.FullName)));
            string installSqlScript = sr.ReadToEnd();

            context.Database.SqlCommand(installSqlScript, new object[] { });
        }
    }

    #endregion
}

Lo script di installazione Install.sql è una risorsa incorporata e conterrà del codice SQL simile al seguente:

IF EXISTS (SELECT [Name] FROM sys.sysobjects WHERE TYPE='U' AND [name]='EdmMetadata') DROP Table EdmMetadata;
IF EXISTS (SELECT [Name] FROM sys.sysobjects WHERE TYPE='U' AND [name]='ProjectDevelopers') DROP Table ProjectDevelopers;
IF EXISTS (SELECT [Name] FROM sys.sysobjects WHERE TYPE='U' AND [name]='Projects') DROP Table Projects;
IF EXISTS (SELECT [Name] FROM sys.sysobjects WHERE TYPE='U' AND [name]='Skills') DROP Table Skills;
IF EXISTS (SELECT [Name] FROM sys.sysobjects WHERE TYPE='U' AND [name]='Developers') DROP Table Developers;
create table [dbo].[Developers] (
    [Id] [int] not null identity,
    [Name] [nvarchar](4000) null,
    [Surname] [nvarchar](4000) null,
    primary key ([Id])
);

Nel Main, al posto di:

DbDatabase.SetInitializer<Db>(new DropCreateDatabaseIfModelChanges<Db>());

è sufficiente scrivere:

DbDatabase.SetInitializer<Db>(new DBCustomInitializerStrategy<Db>());

Print | posted on martedì 4 gennaio 2011 23:56 | Filed Under [ .Net Framework 4.0 Entity Framework 4 ]

Feedback

Gravatar

# re: EF CTP 5: Episode 1 :-)

Complimenti!! Bell'articolo!
05/01/2011 11:32 | max
Gravatar

# re: EF CTP 5: Episode 1 :-)

Grazie ragazzi.
05/01/2011 11:44 | Pietro
Gravatar

# re: EF CTP 5: Episode 1 :-)

Ciao Andrea, fino adesso ho sempre utilizzato EF con il Designer, non ho mai utilizzato un approccio Code First, quindi non ho mai avuto i problemi evidenziati dal tuo commento. Quindi per il momento non abbiamo ancora a che fare con un "prodotto maturo", almeno sotto questo punto di vista.
05/01/2011 12:12 | Pietro
Gravatar

# re: EF CTP 5: Episode 1 :-)

Quindi, ricapitolando: voglio essere figo (Utilizzo Code First così com'è), voglio fare le cose per bene (creo gli oggetti che compongono il Domain Model, creo gli oggetti che compongono il Database, utilizzo uno strato intermedio per l'interazione, ma a questo punto faccio prima ad utilizzare EF nel modo classico).
05/01/2011 12:36 | Pietro
Gravatar

# re: EF CTP 5: Episode 1 :-)

@Andrea
Grazie per i chiarimenti.
05/01/2011 13:00 | Pietro
Gravatar

# EF CTP 5 : Episode II (Data Annotations e Fluent API)

EF CTP 5 : Episode II (Data Annotations e Fluent API)
13/01/2011 09:21 | Il blog di Pietro Libro
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET