Code First to rozwiązanie zaproponowane w EF 4.1, wydanym w kwietniu 2011. Nie jest zatem częścią .NETa 4.0 i VS 2010. Aby skorzystać z tej wersji EF w Visual Studio należy doinstalować najnowszą wersję EF (obecnie 5.0), najlepiej przy użyciu menadżera pakietów NuGet.
W podejściu CodeFirst, modelem danych stają się klasy użytkownika, które można przetransformować na tabele relacyjnej bazy danych. Główna różnica polega na tym, że nie mamy już do czynienia z plikami .edmx. Metadane dla klas tworzone są w pamięci, a nie w plikach xmlowych. Tabele, które zostaną stworzone możemy konfigurować przy użyciu atrybutów nad propercjami lub całymi klasami. Drugie podejście to tzw. Fluent API, czyli konfiguracja poprzez wywołania specjalnych funkcji.
Poniżej prosty przykład jak stworzyć tabelę w CodeFirst. Domyślnie, jeżeli nie skonfigurujemy connectionString, utworzy nam się baza danych w SQL Express o nazwie złożonej z nazwy namespace i nazwy klasy kontekstu.
public class Blog
{
public Blog()
{
Posts = new List<Post>();
}
public int Id { get; set; }
public string Title { get; set; }
public string BloggerName { get; set; }
public List<Post> Posts { get; set; }
public string BlogCode
{
get { return Title.Substring(0, 1) + ":" + BloggerName.Substring(0, 1); }
}
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public virtual List<Blog> Blogs { get; set; }
}
public class Context : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
var db = new Context();
var blog = new Blog() {BloggerName = "Julie", Title = "EFBlog"};
db.Blogs.Add(blog);
db.SaveChanges();
Klasę kontekstu tworzymy dziedzicząc po typie
DbContext wprowadzonym również w EF4.1. Klucze główne dla encji ustawiane są przez konwencję, tak więc np. dla klasy Blog, kluczem głównym będzie property o nazwie Id lub BlogId. Dla tabeli Post utworzony zostanie również domyślnie klucz obcy do tabeli Blog. Ponadto warto skonfigurować program tak, aby przy każdej zmianie modelu baza tworzyła się na nowo. Przy starcie aplikacji dodać należy następujące wyrażenie.
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<Context>());
Gdzie Context to nazwa klasy dziedziczącej po DbContext.
Data Annotations
Data Annotations daje możliwość konfiguracji klas domenowych. W tym podejściu konfiguracja odbywa się poprzez atrybuty. Poniżej lista ciekawszych atrybutów
- [Key] - klucz główny na kolumnie nie obejmowanej konwencją
- [MaxLength(int)] - ustawienie dotyczące ilości znaków dla danej kolumny
- [Required] - mapowane na NOT NULL w SQL
- [Column(String)] - mapowanie na kolumnę o danej nazwie
- [Table(String)] - klasa mapowana na tabelę o podanej nazwie
- [DatabaseGenerated(DatabaseGeneratedOption)] - można tutaj ustawić np Identity dla klucza głównego
- [NotMapped] - nie transformowane do bazy danych
- [ComplexType] - typy złożone jako propercje, poszczególne properties takiego typu zostaną przeklejone do tabeli odpowiadającej encji, w której taki typ się znajduje.
- [ForeignKey(String)] - nazwa klucza obcego umieszczana nad referencją
public class Blog
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public string Title { get; set; }
[MaxLength(30)]
public string BloggerName { get; set; }
public List<Post> Posts { get; set; }
[NotMapped]
public string BlogCode
{
get { return Title.Substring(0, 1) + ":" + BloggerName.Substring(0, 1); }
}
}
Fluent API
Wszystkie opcję dostępne z poziomu DataAnnotations można konfigurować także przy użyciu tzw. FluentAPI, czyli po prostu korzystając z odpowiednich klas i metod.Dzięki takiemu podejściu nasze "Domain Objects" pozostaną niezależne od konfiguracji bazy danych.
public class Context : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
var blog = modelBuilder.Entity<Blog>();
blog.Property(b => b.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
blog.Property(b => b.Title).IsRequired();
blog.Property(b => b.BloggerName).HasMaxLength(30);
blog.Ignore(b => b.BlogCode);
base.OnModelCreating(modelBuilder);
}
}
FluentAPI udostępnia także kilka dodatkowych opcji niedostępnych z poziomu DataAnnotations, na przykład możliwość dzielenia encji na wiele tabel lub łączenia wielu encji w jedną tabelę.