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 II (Data Annotations e Fluent API)

Continuiamo il nostro percorso di studio della CTP 5 di EF, tenendo sempre presente quanto detto nel post precedente o meglio nei commenti. Vediamo come possiamo utilizzare Data Annotations e Fluent API per eseguire l’override delle convenzioni di default usate in code first per mappare la base di dati sottostante: impostare il nome delle tabelle e/o colonne, lunghezza dei campi, definizione delle chiavi, definizione delle relazioni ecc.  La volta precedente, utilizzando Code First generavamo il database rappresentato dal diagramma seguente:

image

Partiamo dal database e apportiamo qualche modifica alle tabelle in questo modo: rinominiamo Projects in Progetto, Skills in Competenza, ProjectsDeveloper in ProgettoSviluppatore  e aggiungiamo una nuova tabella Architetto contenente colonne che estendono le informazioni contenute da Sviluppatore.Rinominiamo anche le colonne in modo da avere anche uno schema di database in italiano:

image

Avendo apportato queste modifiche, se provassimo ad eseguire il codice scritto nel precedente post (utilizzando la strategia di inizializzazione del database custom), verrebbe sollevata un’eccezione System.Data.EntityCommandExecutionException , la quale indica che l’oggetto dbo.Developers non è più valida (ovviamente). Sfruttiamo gli attributi delle Data Annotations: aggiungiamo al nostro progetto un riferimento alla libreria System.ComponentModel.DataAnnotations.dll. Tramite attributi Table(…) istruiamo gli oggetti specificando a quali tabelle devono essere mappati:

[Table("Sviluppatore")]
public class Developer
{
  ...
}

[Table("Progetto")]
public class Project
{
  ...
}

[Table("Competenza")]
public class Skill
{
  ...
}

Se provassimo ad eseguire l’applicazione, avremmo ancora un'eccezione perché la tabella di associazione ProgettoSviluppatore (o meglio ProjectDeveloper) non è conosciuta. Il messaggio dell’eccezione sollevata sarebbe: Invalid object name 'ProjectDeveloper'.  Avremmo comunque diversi problemi dovuti alle operazioni di rename delle colonne nelle tabelle fisiche del database. Dobbiamo  “addrestrare” opportunatamente code first eseguendo l’override del metodo OnModelCreating nella classe  derivata da DbContext (la classe Db).

protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
{
    //Columns Rename.
    modelBuilder.Entity<Developer>()
        .Property(d => d.Name).HasColumnName("Nome");
    modelBuilder.Entity<Developer>()
        .Property(d => d.Surname).HasColumnName("Cognome");

    modelBuilder.Entity<Skill>()
        .Property(s => s.Description).HasColumnName("Descrizione");

    modelBuilder.Entity<Project>()
        .Property(p => p.Description).HasColumnName("Descrizione");

    ////Set many-to-many relationship.
    modelBuilder.Entity<Developer>()
        .HasMany<Project>(d => d.Projects)
        .WithMany(p => p.Developers)
        .Map(m =>
        {
            ////Association table.
            m.ToTable("ProgettoSviluppatore");
            ////Map left Key.
            m.MapLeftKey(d => d.Id, "SviluppatoreId");
            ////Map Right Key.
            m.MapRightKey(p => p.Id, "ProgettoId");
        });

    ////Set relationship between developer and skill.
    modelBuilder.Entity<Developer>()
        .HasMany<Skill>(d => d.Skills)
        .WithOptional()
        .IsIndependent()
        .Map(m => m.MapKey(d => d.Id, "SviluppatoreId"));
}

Modifichiamo il Main:

using (Db db = new Db())
{
    try
    {
        int developers = 0;
        int projects = 0;
        ////Add a new Developer
        Developer dev1 = new Developer()
        {
            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 to 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.Projects select p).Count();

        Console.WriteLine("Projects in db: {0}", developers);
        Console.WriteLine("Developers in db: {0}", developers);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}
ed eseguiamo:
image
Avendo aggiunto la tabella Architetto, abbiamo necessità di aggiungere una nuova classe Architect:
[Table("Architetto")]
public class Architect : Developer
{
    public string Email { get; set; }
    public string Phone { get; set; }
}

modifichiamo il codice di OnModelCreating aggiungendo:

modelBuilder.Entity<Architect>()
  .Property(a => a.Phone).HasColumnName("Telefono");

modifichiamo il Main per aggiungere un Architetto al progetto con competenze più alte dello sviluppatore ( Sorriso ) :

//Add an architect.
Architect arch1 = new Architect()
{
    Name = "Tizio",
    Surname = "Caio",
    Email = "tizio.caio@pm.it",
    Phone = "06123",
    Skills = new Skill[]{
        new Skill (){ Description ="C# 4.0", Rank=5},
        new Skill (){ Description="LINQ", Rank =5},
        new Skill (){ Description="Entity Framework" ,Rank=5},
        new Skill (){ Description="VB.NET 4.0" ,Rank=5},
        new Skill (){ Description="Project Managament" ,Rank=5}
    }
};

prj.Developers.Add(dev1);
dev1.Projects.Add(prj);

prj.Developers.Add(arch1);
arch1.Projects.Add(prj);

db.Developers.Add(dev1);
db.Developers.Add(arch1);

ed eseguiamo. Dovremmo ottenere quanto segue:

image

Si, ok, tutto bello, ma cosa abbiamo fatto ? Tramite Fluent API, diciamo:

1) Il Developer (mappata sulla tabella Sviluppatore, tramite l’attributo Table) ha la proprietà Name mappata sulla colonna Nome (stesso discorso per le colonne delle altre tabelle)

2) Il Developer ha una relazione con molti Project (API HasMany e WithMany) mappata (.Map) sulla tabella (ToTable) ProgettoSviluppatore utilizzando come chiavi (relazione a sinistra e destra) rispettivamente le colonne SviluppatoreId e ProgettoId

3) L’entità Developer ha una relazione con molti Skill, non obbligatoria (WithOptional) , utilizzando come chiave esterna la colonna SviluppatoreId della tabella competenze non esplicitamente indicata (IsIndipendent) nell’entità Skill.

Da sottolineare che tra Developer ed Architect è presenta una strategia di Table-per-type (TPT) Inheritance, in questo caso, automaticamente mappata. Cercheremo di vedere in dettaglio le varie strategie di mapping dell’ereditarietà tra oggetti e tabelle e i possibili tipi di relazione in uno dei prossimi post, insieme alla Validation.

Sicuramente, se si vorrà utilizzare l’approccio code first in progetti reali bisognerà fare molta pratica con Fluent API e Data Annotations per poter scrivere velocemente e senza troppi “errori” il mapping tra classi e tabelle di database. Lo studio continua, ma penso che in casi “complessi” IMHO l’approccio classico tramite Entity Model Designer aiuterebbe a focalizzare in modo veloce tutte le relazioni presenti tra gli oggetti del modello dati.

Print | posted on giovedì 13 gennaio 2011 09:20 | Filed Under [ C# .Net Framework 4.0 Entity Framework 4 ]

Feedback

Gravatar

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

Ottimo, mi sei di grandissimo aiuto.
Grazie!
14/01/2011 02:35 | raffaeu
Gravatar

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

Spero di postare per questo fine settimana l'episode III (forse IV per chiudere la serie). Grazie per il tuo commento.
14/01/2011 11:35 | Pietro
Gravatar

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

Figurati sei stato utile, poche cose ma stai facendo un bel overview del prodotto.
16/01/2011 05:33 | raffaeu
Gravatar

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

GRAZIE
16/01/2011 13:35 | Pietro
Comments have been closed on this topic.

Powered by:
Powered By Subtext Powered By ASP.NET