Share via


ASP.NET MVC Uygulamasında Entity Framework ile Devralma Uygulama (8/10)

tarafından Tom Dykstra

Contoso University örnek web uygulaması, Entity Framework 5 Code First ve Visual Studio 2012 kullanarak ASP.NET MVC 4 uygulamalarının nasıl oluşturulacağını gösterir. Öğretici serisi hakkında bilgi için serideki ilk öğreticiye bakın.

Not

Çözemediğiniz bir sorunla karşılaşırsanız tamamlanmış bölümü indirin ve sorununuzu yeniden oluşturmayı deneyin. Genellikle kodunuzu tamamlanmış kodla karşılaştırarak sorunun çözümünü bulabilirsiniz. Bazı yaygın hatalar ve bunların nasıl çözüldüğü için bkz . Hatalar ve Geçici Çözümler.

Önceki öğreticide eşzamanlılık özel durumlarını işlediyseniz. Bu öğreticide veri modelinde devralmayı nasıl uygulayacağınız gösterilir.

Nesne odaklı programlamada, yedekli kodu ortadan kaldırmak için devralmayı kullanabilirsiniz. Bu öğreticide ve sınıflarını değiştirerek Instructor hem eğitmenler hem de öğrenciler için ortak özellikler içeren LastName bir Person temel sınıftan türetilir.Student Hiçbir web sayfası eklemez veya değiştirmezsiniz, ancak kodun bir bölümünü değiştirirsiniz ve bu değişiklikler otomatik olarak veritabanına yansıtılır.

Hiyerarşi Başına Tablo ve Tür Başına Tablo Devralma

Nesne odaklı programlamada, ilgili sınıflarla çalışmayı kolaylaştırmak için devralmayı kullanabilirsiniz. Örneğin, Instructor veri modelindeki School ve Student sınıfları birkaç özelliği paylaşır ve bu da yedekli kodla sonuçlanan:

Yedekli kodların vurgulandığı Öğrenci ve Eğitmen sınıflarını gösteren ekran görüntüleri.

ve Student varlıkları tarafından Instructor paylaşılan özellikler için yedekli kodu ortadan kaldırmak istediğinizi varsayalım. Yalnızca bu paylaşılan özellikleri içeren bir Person temel sınıf oluşturabilir, ardından aşağıdaki çizimde gösterildiği gibi ve Student varlıklarının bu temel sınıftan devralınır hale getirebilirsinizInstructor:

Person sınıfından türetilen Öğrenci ve Eğitmen sınıflarını gösteren ekran görüntüsü.

Bu devralma yapısının veritabanında temsil edilebileceği çeşitli yollar vardır. Tek bir Person tabloda hem öğrenciler hem de eğitmenler hakkında bilgi içeren bir tablonuz olabilir. Sütunların bazıları yalnızca eğitmenlere (HireDate ), bazıları yalnızca öğrencilere (EnrollmentDate ), bazıları her ikisine de (LastName, FirstName) uygulanabilir. Normalde, her satırın hangi türü temsil ettiğini belirten bir ayırıcı sütuna sahip olursunuz. Örneğin, ayrımcı sütunda eğitmenler için "Eğitmen" ve öğrenciler için "Öğrenci" olabilir.

Person varlık sınıfından devralma yapısını gösteren ekran görüntüsü.

Tek bir veritabanı tablosundan varlık devralma yapısı oluşturma deseni , hiyerarşi başına tablo (TPH) devralma olarak adlandırılır.

Alternatif olarak veritabanının devralma yapısına daha çok benzediğinden emin olabilirsiniz. Örneğin, tabloda yalnızca ad alanları ve tarih alanları Person olan ayrı Instructor ve Student tablolarınız olabilir.

Person varlık sınıfından türetilen yeni Eğitmen ve Öğrenci veritabanı tablolarını gösteren ekran görüntüsü.

Her varlık sınıfı için veritabanı tablosu yapma deseni , tür başına tablo (TPT) devralma olarak adlandırılır.

TPH devralma desenleri genellikle Entity Framework'te TPT devralma desenlerine göre daha iyi performans sunar, çünkü TPT desenleri karmaşık birleştirme sorgularında sonuçlanabilir. Bu öğreticide TPH devralmayı uygulama gösterilmektedir. Bunu yapmak için aşağıdaki adımları gerçekleştireceksiniz:

  • Bir Person sınıf oluşturun ve ve Student sınıflarını öğesinden Persontüretecek şekilde değiştirinInstructor.
  • Veritabanı bağlam sınıfına modelden veritabanına eşleme kodu ekleyin.
  • Proje genelindeki ve StudentID başvurularını olarak PersonIDdeğiştirinInstructorID.

Kişi Sınıfı Oluşturma

Not: Aşağıdaki sınıfları oluşturduktan sonra, bu sınıfları kullanan denetleyicileri güncelleştirene kadar projeyi derleyemezsiniz.

Models klasöründe Person.cs dosyasını oluşturun ve şablon kodunu aşağıdaki kodla değiştirin:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
   public abstract class Person
   {
      [Key]
      public int PersonID { get; set; }

      [RegularExpression(@"^[A-Z]+[a-zA-Z""'\s-]*$")]
      [StringLength(50, MinimumLength = 1)]
      [Display(Name = "Last Name")]
      public string LastName { get; set; }

      [Column("FirstName")]
      [Display(Name = "First Name")]
      [StringLength(50, MinimumLength = 2, ErrorMessage = "First name must be between 2 and 50 characters.")]
      public string FirstMidName { get; set; }

      public string FullName
      {
         get
         {
            return LastName + ", " + FirstMidName;
         }
      }
   }
}

Instructor.cs dosyasında sınıfı sınıfından InstructorPerson türetin ve anahtar ve ad alanlarını kaldırın. Kod aşağıdaki örneğe benzer olacaktır:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
    public class Instructor : Person
    {
        [DataType(DataType.Date)]
        [Display(Name = "Hire Date")]
        public DateTime HireDate { get; set; }

        public virtual ICollection<Course> Courses { get; set; }
        public virtual OfficeAssignment OfficeAssignment { get; set; }
    }
}

Student.cs dosyasında da benzer değişiklikler yapın. Student sınıfı aşağıdaki örneğe benzer olacaktır:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.Models
{
    public class Student : Person
    {
        [DataType(DataType.Date)]
        [Display(Name = "Enrollment Date")]
        public DateTime EnrollmentDate { get; set; }

        public virtual ICollection<Enrollment> Enrollments { get; set; }
    }
}

Modele Kişi Varlık Türünü Ekleme

SchoolContext.cs içinde varlık türü için Person bir DbSet özellik ekleyin:

public DbSet<Person> People { get; set; }

Entity Framework'ün hiyerarşi başına tablo devralmayı yapılandırmak için ihtiyaç duyduğu tek şey budur. Gördüğünüz gibi veritabanı yeniden oluşturulduğunda ve Instructor tablolarının Student yerine bir Person tablo olur.

InstructorID ve StudentID'yi PersonID olarak değiştirme

SchoolContext.cs dosyasındaki Instructor-Course eşleme deyiminde olarak değiştirin MapRightKey("InstructorID")MapRightKey("PersonID"):

modelBuilder.Entity<Course>()
    .HasMany(c => c.Instructors).WithMany(i => i.Courses)
    .Map(t => t.MapLeftKey("CourseID")
    .MapRightKey("PersonID")
    .ToTable("CourseInstructor"));

Bu değişiklik gerekli değildir; yalnızca çoka çok birleştirme tablosundaki InstructorID sütununun adını değiştirir. Adı InstructorID olarak bıraktıysanız uygulama düzgün çalışmaya devam eder. Tamamlanmış SchoolContext.cs aşağıdadır:

using ContosoUniversity.Models;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace ContosoUniversity.DAL
{
   public class SchoolContext : DbContext
   {
      public DbSet<Course> Courses { get; set; }
      public DbSet<Department> Departments { get; set; }
      public DbSet<Enrollment> Enrollments { get; set; }
      public DbSet<Instructor> Instructors { get; set; }
      public DbSet<Student> Students { get; set; }
      public DbSet<OfficeAssignment> OfficeAssignments { get; set; }
      public DbSet<Person> People { get; set; }

      protected override void OnModelCreating(DbModelBuilder modelBuilder)
      {
         modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

         modelBuilder.Entity<Course>()
             .HasMany(c => c.Instructors).WithMany(i => i.Courses)
             .Map(t => t.MapLeftKey("CourseID")
                 .MapRightKey("PersonID")
                 .ToTable("CourseInstructor"));
      }
   }
}

Daha sonra, Geçişler klasöründeki zaman damgalı geçiş dosyaları dışında proje genelinde olarak ve StudentIDPersonID olarak değiştirmeniz InstructorIDPersonID gerekir. Bunu yapmak için yalnızca değiştirilmesi gereken dosyaları bulup açar, ardından açılan dosyalarda genel bir değişiklik yaparsınız. Migrations klasöründe değiştirmeniz gereken tek dosya Migrations\Configuration.cs dosyasıdır.

  1. Önemli

    Visual Studio'daki tüm açık dosyaları kapatarak başlayın.

  2. Düzenle menüsünde Bul ve Değiştir -- Tüm Dosyaları Bul'a tıklayın ve projede içeren InstructorIDtüm dosyaları arayın.

    Bul ve Değiştir penceresini gösteren ekran görüntüsü. Eğitmen I D, Geçerli Proje, Büyük/küçük harf eşleştir ve Tüm sözcüğü eşleştir onay kutuları ve Tümünü Bul düğmesi vurgulanır.

  3. Her dosya için bir satıra çift tıklayarak Migrations klasöründeki time-stamp>_.cs geçiş dosyaları dışındaki<her dosyayı Sonuçları Bul penceresinde açın.

    Sonuçları Bul penceresini gösteren ekran görüntüsü. Zaman damgası geçiş dosyaları kırmızıyla çizilir.

  4. Dosyalarda Değiştir iletişim kutusunu açın ve Bak seçeneğini Tüm Açık Belgeler olarak değiştirin.

  5. Tümünü InstructorID şu şekilde değiştirmek için Dosyalarda Değiştir iletişim kutusunu kullanın:PersonID.

    Bul ve Değiştir penceresini gösteren ekran görüntüsü. D kişisi, Metinle değiştir alanına girilir.

  6. projede içeren StudentIDtüm dosyaları bulun.

  7. Her dosya için bir satıra çift tıklayarak Migrations klasöründeki time-stamp>_*.cs geçiş dosyaları dışındaki<her dosyayı Sonuçları Bul penceresinde açın.

    Sonuçları Bul penceresini gösteren ekran görüntüsü. Zaman damgası geçiş dosyalarının üzeri çizilir.

  8. Dosyalarda Değiştir iletişim kutusunu açın ve Bak seçeneğini Tüm Açık Belgeler olarak değiştirin.

  9. Tümünü StudentIDPersonIDolarak değiştirmek için Dosyalarda Değiştir iletişim kutusunu kullanın.

    Bul ve Değiştir penceresini gösteren ekran görüntüsü. Dosyalar'da Değiştir, Tüm Açık Belgeler, Büyük/küçük harf eşleştir ve Tüm sözcüğü eşleştir onay kutuları ve Tümünü Değiştir düğmesi vurgulanır.

  10. Projeyi derleyin.

(Bunun birincil anahtarları adlandırma deseninin classnameIDbir dezavantajını gösterdiğini unutmayın. Sınıf adına ön ek eklemeden birincil anahtar kimliğini adlandırdıysanız, şimdi yeniden adlandırmaya gerek kalmaz.)

Geçiş Dosyası Oluşturma ve Güncelleştirme

Paket Yöneticisi Konsolu'nda (PMC) aşağıdaki komutu girin:

Add-Migration Inheritance

Update-Database PMC'de komutunu çalıştırın. Geçişlerin nasıl işlendiğini bilmediği mevcut verilerimiz olduğundan komut bu noktada başarısız olur. Aşağıdaki hatayı alıyorsunuz:

ALTER TABLE deyimi FOREIGN KEY kısıtlaması "FK_dbo ile çakışıyor. Department_dbo. Person_PersonID". Çakışma "ContosoUniversity" veritabanında, "dbo" tablosunda oluştu. Person", 'PersonID' sütunu.

Open Migrations< timestamp>_Inheritance.cs ve yöntemini aşağıdaki kodla değiştirinUp:

public override void Up()
{
    DropForeignKey("dbo.Department", "InstructorID", "dbo.Instructor");
    DropForeignKey("dbo.OfficeAssignment", "InstructorID", "dbo.Instructor");
    DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
    DropForeignKey("dbo.CourseInstructor", "InstructorID", "dbo.Instructor");
    DropIndex("dbo.Department", new[] { "InstructorID" });
    DropIndex("dbo.OfficeAssignment", new[] { "InstructorID" });
    DropIndex("dbo.Enrollment", new[] { "StudentID" });
    DropIndex("dbo.CourseInstructor", new[] { "InstructorID" });
    RenameColumn(table: "dbo.Department", name: "InstructorID", newName: "PersonID");
    RenameColumn(table: "dbo.OfficeAssignment", name: "InstructorID", newName: "PersonID");
    RenameColumn(table: "dbo.Enrollment", name: "StudentID", newName: "PersonID");
    RenameColumn(table: "dbo.CourseInstructor", name: "InstructorID", newName: "PersonID");
    CreateTable(
        "dbo.Person",
        c => new
            {
                PersonID = c.Int(nullable: false, identity: true),
                LastName = c.String(maxLength: 50),
                FirstName = c.String(maxLength: 50),
                HireDate = c.DateTime(),
                EnrollmentDate = c.DateTime(),
                Discriminator = c.String(nullable: false, maxLength: 128),
                OldId = c.Int(nullable: false)
            })
        .PrimaryKey(t => t.PersonID);

    // Copy existing Student and Instructor data into new Person table.
    Sql("INSERT INTO dbo.Person (LastName, FirstName, HireDate, EnrollmentDate, Discriminator, OldId) SELECT LastName, FirstName, null AS HireDate, EnrollmentDate, 'Student' AS Discriminator, StudentId AS OldId FROM dbo.Student");
    Sql("INSERT INTO dbo.Person (LastName, FirstName, HireDate, EnrollmentDate, Discriminator, OldId) SELECT LastName, FirstName, HireDate, null AS EnrollmentDate, 'Instructor' AS Discriminator, InstructorId AS OldId FROM dbo.Instructor");

    // Fix up existing relationships to match new PK's.
    Sql("UPDATE dbo.Enrollment SET PersonId = (SELECT PersonId FROM dbo.Person WHERE OldId = Enrollment.PersonId AND Discriminator = 'Student')");
    Sql("UPDATE dbo.Department SET PersonId = (SELECT PersonId FROM dbo.Person WHERE OldId = Department.PersonId AND Discriminator = 'Instructor')");
    Sql("UPDATE dbo.OfficeAssignment SET PersonId = (SELECT PersonId FROM dbo.Person WHERE OldId = OfficeAssignment.PersonId AND Discriminator = 'Instructor')");
    Sql("UPDATE dbo.CourseInstructor SET PersonId = (SELECT PersonId FROM dbo.Person WHERE OldId = CourseInstructor.PersonId AND Discriminator = 'Instructor')");

    // Remove temporary key
    DropColumn("dbo.Person", "OldId");

    AddForeignKey("dbo.Department", "PersonID", "dbo.Person", "PersonID");
    AddForeignKey("dbo.OfficeAssignment", "PersonID", "dbo.Person", "PersonID");
    AddForeignKey("dbo.Enrollment", "PersonID", "dbo.Person", "PersonID", cascadeDelete: true);
    AddForeignKey("dbo.CourseInstructor", "PersonID", "dbo.Person", "PersonID", cascadeDelete: true);
    CreateIndex("dbo.Department", "PersonID");
    CreateIndex("dbo.OfficeAssignment", "PersonID");
    CreateIndex("dbo.Enrollment", "PersonID");
    CreateIndex("dbo.CourseInstructor", "PersonID");
    DropTable("dbo.Instructor");
    DropTable("dbo.Student");
}

update-database komutunu yeniden çalıştırın.

Not

Verileri geçirirken ve şema değişiklikleri yaparken başka hatalar almak mümkündür. Çözemediğiniz geçiş hataları alırsanız, Web.config dosyasındaki bağlantı dizesi değiştirerek veya veritabanını silerek öğreticiye devam edebilirsiniz. En basit yaklaşım, veritabanınıWeb.config dosyasında yeniden adlandırmaktır. Örneğin, aşağıdaki örnekte gösterildiği gibi veritabanı adını CU_test olarak değiştirin:

<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=CU_Test;
      Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\CU_Test.mdf" 
      providerName="System.Data.SqlClient" />

Yeni bir veritabanıyla geçirecek veri yoktur ve komutun update-database hatasız tamamlanma olasılığı çok daha yüksektir. Veritabanını silme yönergeleri için bkz. Visual Studio 2012'den Veritabanı Bırakma. Öğreticiye devam etmek için bu yaklaşımı benimserseniz, dağıtılan site geçişleri otomatik olarak çalıştırdığında aynı hatayı alacağı için bu öğreticinin sonundaki dağıtım adımını atlayın. Geçiş hatasını gidermek istiyorsanız, en iyi kaynak Entity Framework forumlarından veya StackOverflow.com biridir.

Test Etme

Siteyi çalıştırın ve çeşitli sayfaları deneyin. Her şey daha önce olduğu gibi çalışır.

Sunucu Gezgini'ndeSchoolContext'i ve ardından Tablolar'ı genişletin; Öğrenci ve Eğitmen tablolarının bir Kişi tablosuyla değiştirildiğini görürsünüz. Kişi tablosunu genişletirseniz, öğrenci ve eğitmen tablolarında kullanılan tüm sütunların bulunduğunu görürsünüz.

Sunucu Gezgini penceresini gösteren ekran görüntüsü. Veri Bağlantıları, Okul Bağlamı ve Tablolar sekmeleri, Kişi tablosunu gösterecek şekilde genişletilir.

Kişi tablosuna sağ tıklayın ve sonra ayrımcı sütunu görmek için Tablo Verilerini Göster'e tıklayın.

Kişi tablosunu gösteren ekran görüntüsü. Ayırıcı sütun adı vurgulanır.

Aşağıdaki diyagramda yeni School veritabanının yapısı gösterilmektedir:

Okul veritabanı diyagramını gösteren ekran görüntüsü.

Özet

Hiyerarşi başına tablo devralma artık , Studentve Instructor sınıfları için Personuygulanmıştır. Bu ve diğer devralma yapıları hakkında daha fazla bilgi için Morteza Manavi'nin blogundaki Devralma Eşleme Stratejileri'ne bakın. Sonraki öğreticide depo ve iş birimi desenlerini uygulamanın bazı yollarını göreceksiniz.

Diğer Entity Framework kaynaklarına bağlantılar ASP.NET Veri Erişimi İçerik Eşlemesi'nde bulunabilir.