Entity Framework - pierwszy przykład

Zdefiniujmy bardzo prosty model za pomocą klas. Po prostu definiujemy je w pliku Program.cs, ale w rzeczywistej aplikacji podzielisz swoje klasy na osobne pliki i potencjalnie na oddzielny projekt. Poniżej znajduje się model danych, który będziemy tworzyć przy użyciu podejścia Code First.

Utwórz model

Dodaj następujące trzy klasy w pliku Program.cs, używając następującego kodu dla klasy Student.

public class Student {
   public int ID { get; set; }
   public string LastName { get; set; }
   public string FirstMidName { get; set; }
   public DateTime EnrollmentDate { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}
  • Właściwość ID stanie się kolumną klucza podstawowego tabeli bazy danych, która odpowiada tej klasie.

  • Właściwość Enrollments jest właściwością nawigacji. Właściwości nawigacji zawierają inne jednostki, które są powiązane z tą jednostką.

  • W takim przypadku właściwość Enrollments jednostki Student będzie zawierać wszystkie jednostki Enrollment powiązane z tą jednostką Student.

  • Właściwości nawigacji są zwykle definiowane jako wirtualne, dzięki czemu mogą korzystać z niektórych funkcji Entity Framework, takich jak ładowanie z opóźnieniem.

  • Jeśli właściwość nawigacji może zawierać wiele jednostek (jak w relacjach wiele do wielu lub jeden do wielu), jej typem musi być lista, na której można dodawać, usuwać i aktualizować wpisy, na przykład ICollection.

Poniżej znajduje się implementacja dla zajęć Course.

public class Course {
   public int CourseID { get; set; }
   public string Title { get; set; }
   public int Credits { get; set; }
	
   public virtual ICollection<Enrollment> Enrollments { get; set; }
}

Właściwość Enrollments jest właściwością nawigacji. Jednostka kursu może być powiązana z dowolną liczbą jednostek rejestracji.

Poniżej przedstawiono implementację klasy Enrollment i wyliczenia.

public enum Grade {
   A, B, C, D, F
}

public class Enrollment {
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }
	
   public virtual Course Course { get; set; }
   public virtual Student Student { get; set; }
}
  • Właściwość EnrollmentID będzie kluczem podstawowym.

  • Właściwość Grade jest wyliczeniem. Znak zapytania po deklaracji typu Grade wskazuje, że właściwość Grade ma wartość null.

  • Ocena zerowa różni się od oceny zerowej. Null oznacza, że ​​ocena nie jest znana lub nie została jeszcze przypisana.

  • Właściwości StudentID i CourseID są kluczami obcymi, a odpowiednie właściwości nawigacji to Student i Course.

  • Encja Enrollment jest powiązana z jedną jednostką Student i jedną Course, więc nieruchomość może zawierać tylko jedną jednostkę Student i Course.

Utwórz kontekst bazy danych

Główną klasą koordynującą funkcjonalność Entity Framework dla danego modelu danych jest klasa kontekstu bazy danych, która umożliwia wykonywanie zapytań i zapisywanie danych. Tę klasę można utworzyć, wyprowadzając z klasy DbContext i uwidaczniając wpisaną DbSet dla każdej klasy w naszym modelu. Poniżej przedstawiono implementację klasy MyContext, która jest pochodną klasy DbContext.

public class MyContext : DbContext {
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Poniżej znajduje się pełny kod w pliku Program.cs.

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EFCodeFirstDemo {

   class Program {
      static void Main(string[] args) {}
   }

   public enum Grade {
      A, B, C, D, F
   }

   public class Enrollment {
      public int EnrollmentID { get; set; }
      public int CourseID { get; set; }
      public int StudentID { get; set; }
      public Grade? Grade { get; set; }
		
      public virtual Course Course { get; set; }
      public virtual Student Student { get; set; }
   }

   public class Student {
      public int ID { get; set; }
      public string LastName { get; set; }
      public string FirstMidName { get; set; }
      public DateTime EnrollmentDate { get; set; }
		
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class Course {
      public int CourseID { get; set; }
      public string Title { get; set; }
      public int Credits { get; set; }
		
      public virtual ICollection<Enrollment> Enrollments { get; set; }
   }

   public class MyContext : DbContext {
      public virtual DbSet<Course> Courses { get; set; }
      public virtual DbSet<Enrollment> Enrollments { get; set; }
      public virtual DbSet<Student> Students { get; set; }
   }

}

Powyższy kod to wszystko, czego potrzebujemy, aby rozpocząć przechowywanie i pobieranie danych. Dodajmy trochę danych, a następnie je pobierzmy. Poniżej znajduje się kod w metodzie main.

static void Main(string[] args) {

   using (var context = new MyContext()) {
      // Create and save a new Students
      Console.WriteLine("Adding new students");

      var student = new Student {
         FirstMidName = "Alain", LastName = "Bomer", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student);
		
      var student1 = new Student {
         FirstMidName = "Mark", LastName = "Upston", 
            EnrollmentDate = DateTime.Parse(DateTime.Today.ToString())
      };

      context.Students.Add(student1);
      context.SaveChanges();

      // Display all Students from the database
      var students = (from s in context.Students 
         orderby s.FirstMidName select s).ToList<Student>();

      Console.WriteLine("Retrieve all Students from the database:");

      foreach (var stdnt in students) {
         string name = stdnt.FirstMidName + " " + stdnt.LastName;
         Console.WriteLine("ID: {0}, Name: {1}", stdnt.ID, name);
      }
		
      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
   }
}

Po wykonaniu powyższego kodu otrzymasz następujące dane wyjściowe.

Adding new students
Retrieve all Students from the database:
ID: 1, Name: Alain Bomer
ID: 2, Name: Mark Upston
Press any key to exit...

Teraz przychodzi mi na myśl pytanie, gdzie są dane i baza danych, do której dodaliśmy dane, a następnie pobraliśmy je z bazy danych. Zgodnie z konwencją DbContext utworzył dla Ciebie bazę danych.

  • Jeśli lokalne wystąpienie SQL Express jest dostępne, Code First utworzyło bazę danych w tym wystąpieniu.

  • Jeśli SQL Express nie jest dostępny, Code First spróbuje użyć LocalDb.

  • Nazwa bazy danych pochodzi od w pełni kwalifikowanej nazwy kontekstu pochodnego.

W naszym przypadku instancja SQL Express jest dostępna, a nazwa bazy danych to EFCodeFirstDemo.MyContext, jak pokazano na poniższej ilustracji.

  • Są to tylko domyślne konwencje i istnieją różne sposoby zmiany bazy danych używanej przez Code First.

  • Jak widać na powyższym obrazku, utworzono tabele studentów, kursów i zapisów, a każda tabela zawiera kolumny o odpowiednim typie danych i długości.

  • Nazwy kolumn i typ danych są również zgodne z właściwościami odpowiednich klas domeny.

Inicjalizacja bazy danych

W powyższym przykładzie widzieliśmy, że Code First automatycznie tworzy bazę danych, ale jeśli chcesz zmienić nazwę bazy danych i serwera, zobaczmy, jak Code First decyduje o nazwie bazy danych i serwerze podczas inicjowania bazy danych. Spójrz na poniższy diagram.

Możesz zdefiniować konstruktora bazowego klasy kontekstu w następujący sposób.

  • Brak parametru
  • Nazwa bazy danych
  • Nazwa ciągu połączenia

Brak parametru

Jeśli określisz konstruktora podstawowego klasy kontekstu bez żadnego parametru, jak pokazano w powyższym przykładzie, struktura encji utworzy bazę danych na lokalnym serwerze SQLEXPRESS o nazwie {Namespace}. {Context class name}.

W powyższym przykładzie tworzona automatycznie baza danych ma nazwę EFCodeFirstDemo.MyContext. Jeśli spojrzysz na nazwę, zobaczysz, że EFCodeFirstDemo to przestrzeń nazw, a MyContext to nazwa klasy kontekstu, jak pokazano w poniższym kodzie.

public class MyContext : DbContext {
   public MyContext() : base() {}

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Nazwa bazy danych

Jeśli przekażesz nazwę bazy danych jako parametr w konstruktorze podstawowym klasy kontekstu, Code First automatycznie ponownie utworzy bazę danych, ale tym razem nazwa będzie tą przekazaną jako parametr w konstruktorze podstawowym na lokalnym serwerze bazy danych SQLEXPRESS .

W poniższym kodzie MyContextDB jest określony jako parametr w konstruktorze podstawowym. Jeśli uruchomisz swoją aplikację, baza danych o nazwie MyContextDB zostanie utworzona na lokalnym serwerze SQL.

public class MyContext : DbContext {
   public MyContext() : base("MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}

Nazwa ciągu połączenia

Jest to łatwy sposób na poinformowanie DbContext, aby używał serwera bazy danych innego niż SQL Express lub LocalDb. Możesz zdecydować się na umieszczenie parametrów połączenia w pliku app.config.

  • Jeśli nazwa parametrów połączenia jest zgodna z nazwą kontekstu (z kwalifikacją przestrzeni nazw lub bez niej), zostanie ona znaleziona przez DbContext, gdy zostanie użyty konstruktor bez parametru.

  • Jeśli nazwa parametrów połączenia różni się od nazwy kontekstu, możesz powiedzieć DbContext, aby używał tego połączenia w trybie Code First, przekazując nazwę parametrów połączenia do konstruktora DbContext.

public class MyContext : DbContext {
   public MyContext() : base("name = MyContextDB") {}
   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}
  • W powyższym kodzie fragment parametrów połączenia klasy kontekstu jest określony jako parametr w konstruktorze podstawowym.

  • Nazwa ciągu połączenia musi zaczynać się od „nazwa =”, w przeciwnym razie będzie traktowana jako nazwa bazy danych.

  • Ten formularz wyraźnie określa, że ​​oczekujesz, że parametry połączenia zostaną znalezione w pliku konfiguracyjnym. Wyjątek zostanie zgłoszony, jeśli nie zostaną znalezione parametry połączenia o podanej nazwie.

<connectionStrings>
   <add name = "MyContextDB"
      connectionString = "Data Source =.;Initial Catalog = EFMyContextDB;Integrated Security = true"
      providerName = "System.Data.SqlClient"/>
</connectionStrings>
  • Nazwa bazy danych w parametrach połączenia w app.config to EFMyContextDB. CodeFirst utworzy nowy plikEFMyContextDB baza danych lub użyj istniejącej EFMyContextDB baza danych na lokalnym serwerze SQL.

Klasy domeny

Do tej pory po prostu pozwoliliśmy EF odkryć model przy użyciu jego domyślnych konwencji, ale będą chwile, kiedy nasze klasy nie będą zgodne z konwencjami i będziemy musieli mieć możliwość wykonania dalszej konfiguracji. Możesz jednak zastąpić te konwencje, konfigurując klasy domeny, aby zapewnić EF informacje, których potrzebuje. Istnieją dwie opcje konfigurowania klas domeny -

  • Adnotacje danych
  • Fluent API

Adnotacje danych

DataAnnotations służy do konfigurowania klas, które będą podkreślać najczęściej potrzebne konfiguracje. Adnotacje danych są również rozumiane przez wiele aplikacji .NET, takich jak ASP.NET MVC, które umożliwiają tym aplikacjom wykorzystanie tych samych adnotacji do walidacji po stronie klienta.

Poniżej znajdują się adnotacje danych używane w klasie uczniów.

public class Enrollment {

   [Key]
   public int EnrollmentID { get; set; }
   public int CourseID { get; set; }
   public int StudentID { get; set; }
   public Grade? Grade { get; set; }

   [ForeignKey("CourseID")]
   public virtual Course Course { get; set; }

   [ForeignKey("ID")]
   public virtual Student Student { get; set; }
}

Fluent API

Większość konfiguracji modelu można wykonać za pomocą prostych adnotacji danych. Fluent API to zaawansowany sposób określania konfiguracji modelu, który obejmuje wszystko, co mogą zrobić adnotacje danych, oprócz bardziej zaawansowanej konfiguracji, której nie można wykonać w przypadku adnotacji danych. Adnotacje danych i Fluent API mogą być używane razem.

Aby uzyskać dostęp do interfejsu API Fluent, należy zastąpić metodę OnModelCreating w DbContext. Teraz zmieńmy nazwę kolumny w tabeli uczniów z FirstMidName na FirstName, jak pokazano w poniższym kodzie.

public class MyContext : DbContext {

   protected override void OnModelCreating(DbModelBuilder modelBuilder) {
      modelBuilder.Entity<Student>().Property(s ⇒ s.FirstMidName)
         .HasColumnName("FirstName");
   }

   public virtual DbSet<Course> Courses { get; set; }
   public virtual DbSet<Enrollment> Enrollments { get; set; }
   public virtual DbSet<Student> Students { get; set; }
}