Bagikan melalui


API Fasih - Mengonfigurasi dan Memetakan Properti dan Jenis

Saat bekerja dengan Kode Kerangka Kerja Entitas Pertama, perilaku defaultnya adalah memetakan kelas POCO Anda ke tabel menggunakan serangkaian konvensi yang dipanggang ke dalam EF. Namun, terkadang, Anda tidak dapat atau tidak ingin mengikuti konvensi tersebut dan perlu memetakan entitas ke sesuatu selain apa yang didikte konvensi.

Ada dua cara utama Anda dapat mengonfigurasi EF untuk menggunakan sesuatu selain konvensi, yaitu anotasi atau API fasih EF. Anotasi hanya mencakup subset fungsionalitas API yang fasih, sehingga ada skenario pemetaan yang tidak dapat dicapai menggunakan anotasi. Artikel ini dirancang untuk menunjukkan cara menggunakan API fasih untuk mengonfigurasi properti.

API fasih pertama kode paling umum diakses dengan mengesampingkan metode OnModelCreating pada DbContext turunan Anda. Sampel berikut dirancang untuk menunjukkan cara melakukan berbagai tugas dengan api yang fasih dan memungkinkan Anda menyalin kode dan menyesuaikannya agar sesuai dengan model Anda, jika Anda ingin melihat model yang dapat digunakan sebagaimana adanya maka disediakan di akhir artikel ini.

Pengaturan Di Seluruh Model

Skema Default (EF6 dan seterusnya)

Dimulai dengan EF6 Anda dapat menggunakan metode HasDefaultSchema pada DbModelBuilder untuk menentukan skema database yang akan digunakan untuk semua tabel, prosedur tersimpan, dll. Pengaturan default ini akan diganti untuk objek apa pun yang secara eksplisit Anda konfigurasikan skema yang berbeda.

modelBuilder.HasDefaultSchema("sales");

Konvensi Kustom (EF6 dan seterusnya)

Dimulai dengan EF6 Anda dapat membuat konvensi Anda sendiri untuk melengkapi konvensi yang disertakan dalam Kode Pertama. Untuk detail selengkapnya, lihat Konvensi Pertama Kode Kustom.

Pemetaan Properti

Metode Properti digunakan untuk mengonfigurasi atribut untuk setiap properti milik entitas atau jenis kompleks. Metode Properti digunakan untuk mendapatkan objek konfigurasi untuk properti tertentu. Opsi pada objek konfigurasi khusus untuk jenis yang dikonfigurasi; IsUnicode hanya tersedia pada properti string misalnya.

Mengonfigurasi Kunci Primer

Konvensi Kerangka Kerja Entitas untuk kunci primer adalah:

  1. Kelas Anda menentukan properti yang namanya "ID" atau "Id"
  2. atau nama kelas diikuti dengan "ID" atau "Id"

Untuk secara eksplisit mengatur properti menjadi kunci utama, Anda dapat menggunakan metode HasKey. Dalam contoh berikut, metode HasKey digunakan untuk mengonfigurasi kunci utama InstructorID pada jenis OfficeAssignment.

modelBuilder.Entity<OfficeAssignment>().HasKey(t => t.InstructorID);

Mengonfigurasi Kunci Primer Komposit

Contoh berikut mengonfigurasi properti DepartmentID dan Name untuk menjadi kunci primer komposit dari jenis Departemen.

modelBuilder.Entity<Department>().HasKey(t => new { t.DepartmentID, t.Name });

Mematikan Identitas untuk Kunci Primer Numerik

Contoh berikut mengatur properti DepartmentID ke System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.None untuk menunjukkan bahwa nilai tidak akan dihasilkan oleh database.

modelBuilder.Entity<Department>().Property(t => t.DepartmentID)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

Menentukan Panjang Maksimum pada Properti

Dalam contoh berikut, properti Nama harus tidak lebih dari 50 karakter. Jika Anda membuat nilai lebih panjang dari 50 karakter, Anda akan mendapatkan pengecualian DbEntityValidationException . Jika Code First membuat database dari model ini, kode juga akan mengatur panjang maksimum kolom Nama menjadi 50 karakter.

modelBuilder.Entity<Department>().Property(t => t.Name).HasMaxLength(50);

Mengonfigurasi Properti yang Diperlukan

Dalam contoh berikut, properti Nama diperlukan. Jika Anda tidak menentukan Nama, Anda akan mendapatkan pengecualian DbEntityValidationException. Jika Code First membuat database dari model ini, maka kolom yang digunakan untuk menyimpan properti ini biasanya tidak akan dapat diubah ke null.

Catatan

Dalam beberapa kasus, kolom dalam database mungkin tidak dapat diubah ke null meskipun properti diperlukan. Misalnya, saat menggunakan data strategi pewarisan TPH untuk beberapa jenis disimpan dalam satu tabel. Jika jenis turunan menyertakan properti yang diperlukan, kolom tidak dapat dibuat tidak dapat diubah ke null karena tidak semua jenis dalam hierarki akan memiliki properti ini.

modelBuilder.Entity<Department>().Property(t => t.Name).IsRequired();

Mengonfigurasi Indeks pada satu atau beberapa properti

Catatan

EF6.1 Onwards Only - Atribut Indeks diperkenalkan dalam Entity Framework 6.1. Jika Anda menggunakan versi yang lebih lama, informasi di bagian ini tidak berlaku.

Membuat indeks tidak didukung secara asli oleh API Fasih, tetapi Anda dapat menggunakan dukungan untuk IndexAttribute melalui API Fasih. Atribut indeks diproses dengan menyertakan anotasi model pada model yang kemudian diubah menjadi Indeks dalam database nanti di alur. Anda dapat menambahkan anotasi yang sama ini secara manual menggunakan API Fasih.

Cara term mudah untuk melakukan ini adalah dengan membuat instans IndexAttribute yang berisi semua pengaturan untuk indeks baru. Anda kemudian dapat membuat instans IndexAnnotation yang merupakan jenis spesifik EF yang akan mengonversi pengaturan IndexAttribute menjadi anotasi model yang dapat disimpan pada model EF. Ini kemudian dapat diteruskan ke metode HasColumnAnnotation pada API Fasih, menentukan nama Indeks untuk anotasi.

modelBuilder
    .Entity<Department>()
    .Property(t => t.Name)
    .HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute()));

Untuk daftar lengkap pengaturan yang tersedia di IndexAttribute, lihat bagian Indeks Anotasi Data Pertama Kode. Ini termasuk menyesuaikan nama indeks, membuat indeks unik, dan membuat indeks multi-kolom.

Anda dapat menentukan beberapa anotasi indeks pada satu properti dengan meneruskan array IndexAttribute ke konstruktor IndexAnnotation.

modelBuilder
    .Entity<Department>()
    .Property(t => t.Name)
    .HasColumnAnnotation(
        "Index",  
        new IndexAnnotation(new[]
            {
                new IndexAttribute("Index1"),
                new IndexAttribute("Index2") { IsUnique = true }
            })));

Menentukan Tidak memetakan Properti CLR ke Kolom di Database

Contoh berikut menunjukkan cara menentukan bahwa properti pada jenis CLR tidak dipetakan ke kolom dalam database.

modelBuilder.Entity<Department>().Ignore(t => t.Budget);

Memetakan Properti CLR ke Kolom Tertentu di Database

Contoh berikut memetakan properti Name CLR ke kolom database DepartmentName.

modelBuilder.Entity<Department>()
    .Property(t => t.Name)
    .HasColumnName("DepartmentName");

Mengganti Nama Kunci Asing yang Tidak Ditentukan dalam Model

Jika Anda memilih untuk tidak menentukan kunci asing pada jenis CLR, tetapi ingin menentukan nama apa yang harus dimilikinya dalam database, lakukan hal berikut:

modelBuilder.Entity<Course>()
    .HasRequired(c => c.Department)
    .WithMany(t => t.Courses)
    .Map(m => m.MapKey("ChangedDepartmentID"));

Mengonfigurasi apakah Properti String Mendukung Konten Unicode

Secara default string adalah Unicode (nvarchar di SQL Server). Anda dapat menggunakan metode IsUnicode untuk menentukan bahwa string harus berjenis varchar.

modelBuilder.Entity<Department>()
    .Property(t => t.Name)
    .IsUnicode(false);

Mengonfigurasi Tipe Data Kolom Database

Metode HasColumnType memungkinkan pemetaan ke representasi yang berbeda dari jenis dasar yang sama. Menggunakan metode ini tidak memungkinkan Anda melakukan konversi data apa pun pada waktu proses. Perhatikan bahwa IsUnicode adalah cara yang lebih disukai untuk mengatur kolom ke varchar, karena ini adalah agnostik database.

modelBuilder.Entity<Department>()   
    .Property(p => p.Name)   
    .HasColumnType("varchar");

Mengonfigurasi Properti pada Jenis Kompleks

Ada dua cara untuk mengonfigurasi properti skalar pada jenis kompleks.

Anda dapat memanggil Properti di ComplexTypeConfiguration.

modelBuilder.ComplexType<Details>()
    .Property(t => t.Location)
    .HasMaxLength(20);

Anda juga dapat menggunakan notasi titik untuk mengakses properti dari jenis kompleks.

modelBuilder.Entity<OnsiteCourse>()
    .Property(t => t.Details.Location)
    .HasMaxLength(20);

Mengonfigurasi Properti yang Akan Digunakan sebagai Token Konkurensi Optimis

Untuk menentukan bahwa properti dalam entitas mewakili token konkurensi, Anda dapat menggunakan atribut ConcurrencyCheck atau metode IsConcurrencyToken.

modelBuilder.Entity<OfficeAssignment>()
    .Property(t => t.Timestamp)
    .IsConcurrencyToken();

Anda juga dapat menggunakan metode IsRowVersion untuk mengonfigurasi properti menjadi versi baris dalam database. Mengatur properti menjadi versi baris secara otomatis mengonfigurasinya menjadi token konkurensi optimis.

modelBuilder.Entity<OfficeAssignment>()
    .Property(t => t.Timestamp)
    .IsRowVersion();

Pemetaan Jenis

Menentukan Bahwa Kelas Adalah Jenis Kompleks

Menurut konvensi, jenis yang tidak memiliki kunci primer yang ditentukan diperlakukan sebagai jenis kompleks. Ada beberapa skenario di mana Code First tidak akan mendeteksi jenis kompleks (misalnya, jika Anda memiliki properti yang disebut ID, tetapi Anda tidak bermaksud untuk itu menjadi kunci utama). Dalam kasus seperti itu, Anda akan menggunakan API fasih untuk secara eksplisit menentukan bahwa jenis adalah jenis yang kompleks.

modelBuilder.ComplexType<Details>();

Menentukan Tidak memetakan Tipe Entitas CLR ke Tabel di Database

Contoh berikut menunjukkan cara mengecualikan jenis CLR agar tidak dipetakan ke tabel dalam database.

modelBuilder.Ignore<OnlineCourse>();

Memetakan Jenis Entitas ke Tabel Tertentu di Database

Semua properti Departemen akan dipetakan ke kolom dalam tabel yang disebut departemen t_.

modelBuilder.Entity<Department>()  
    .ToTable("t_Department");

Anda juga dapat menentukan nama skema seperti ini:

modelBuilder.Entity<Department>()  
    .ToTable("t_Department", "school");

Memetakan Warisan Table-Per-Hierarchy (TPH)

Dalam skenario pemetaan TPH, semua jenis dalam hierarki warisan dipetakan ke satu tabel. Kolom diskriminator digunakan untuk mengidentifikasi jenis setiap baris. Saat membuat model Anda dengan Code First, TPH adalah strategi default untuk jenis yang berpartisipasi dalam hierarki warisan. Secara default, kolom diskriminator ditambahkan ke tabel dengan nama "Diskriminator" dan nama jenis CLR dari setiap jenis dalam hierarki digunakan untuk nilai diskriminator. Anda dapat mengubah perilaku default dengan menggunakan API yang fasih.

modelBuilder.Entity<Course>()  
    .Map<Course>(m => m.Requires("Type").HasValue("Course"))  
    .Map<OnsiteCourse>(m => m.Requires("Type").HasValue("OnsiteCourse"));

Memetakan Warisan Tabel Per Jenis (TPT)

Dalam skenario pemetaan TPT, semua jenis dipetakan ke tabel individual. Properti yang hanya termasuk dalam jenis dasar atau jenis turunan disimpan dalam tabel yang memetakan ke jenis tersebut. Tabel yang memetakan ke jenis turunan juga menyimpan kunci asing yang menggabungkan tabel turunan dengan tabel dasar.

modelBuilder.Entity<Course>().ToTable("Course");  
modelBuilder.Entity<OnsiteCourse>().ToTable("OnsiteCourse");

Memetakan Warisan Kelas Tabel Per Beton (TPC)

Dalam skenario pemetaan TPC, semua jenis non-abstrak dalam hierarki dipetakan ke tabel individual. Tabel yang memetakan ke kelas turunan tidak memiliki hubungan dengan tabel yang memetakan ke kelas dasar dalam database. Semua properti kelas, termasuk properti yang diwariskan, dipetakan ke kolom tabel terkait.

Panggil metode MapInheritedProperties untuk mengonfigurasi setiap jenis turunan. MapInheritedProperties memetakan ulang semua properti yang diwarisi dari kelas dasar ke kolom baru dalam tabel untuk kelas turunan.

Catatan

Perhatikan bahwa karena tabel yang berpartisipasi dalam hierarki pewarisan TPC tidak berbagi kunci utama akan ada kunci entitas duplikat saat menyisipkan dalam tabel yang dipetakan ke subkelas jika Anda memiliki nilai yang dihasilkan database dengan seed identitas yang sama. Untuk mengatasi masalah ini, Anda dapat menentukan nilai awal yang berbeda untuk setiap tabel atau menonaktifkan identitas pada properti kunci utama. Identitas adalah nilai default untuk properti kunci bilangan bulat saat bekerja dengan Kode Pertama.

modelBuilder.Entity<Course>()
    .Property(c => c.CourseID)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

modelBuilder.Entity<OnsiteCourse>().Map(m =>
{
    m.MapInheritedProperties();
    m.ToTable("OnsiteCourse");
});

modelBuilder.Entity<OnlineCourse>().Map(m =>
{
    m.MapInheritedProperties();
    m.ToTable("OnlineCourse");
});

Memetakan Properti Jenis Entitas ke Beberapa Tabel dalam Database (Pemisahan Entitas)

Pemisahan entitas memungkinkan properti jenis entitas tersebar di beberapa tabel. Dalam contoh berikut, entitas Departemen dibagi menjadi dua tabel: Departemen dan DepartemenDetails. Pemisahan entitas menggunakan beberapa panggilan ke metode Peta untuk memetakan subset properti ke tabel tertentu.

modelBuilder.Entity<Department>()
    .Map(m =>
    {
        m.Properties(t => new { t.DepartmentID, t.Name });
        m.ToTable("Department");
    })
    .Map(m =>
    {
        m.Properties(t => new { t.DepartmentID, t.Administrator, t.StartDate, t.Budget });
        m.ToTable("DepartmentDetails");
    });

Memetakan Beberapa Jenis Entitas ke Satu Tabel dalam Database (Pemisahan Tabel)

Contoh berikut memetakan dua jenis entitas yang berbagi kunci utama ke satu tabel.

modelBuilder.Entity<OfficeAssignment>()
    .HasKey(t => t.InstructorID);

modelBuilder.Entity<Instructor>()
    .HasRequired(t => t.OfficeAssignment)
    .WithRequiredPrincipal(t => t.Instructor);

modelBuilder.Entity<Instructor>().ToTable("Instructor");

modelBuilder.Entity<OfficeAssignment>().ToTable("Instructor");

Memetakan Jenis Entitas untuk Menyisipkan/Memperbarui/Menghapus Prosedur Tersimpan (EF6 dan seterusnya)

Dimulai dengan EF6 Anda dapat memetakan entitas untuk menggunakan prosedur tersimpan untuk menyisipkan pembaruan dan penghapusan. Untuk detail selengkapnya lihat, Kode Pertama Sisipkan/Perbarui/Hapus Prosedur Tersimpan.

Model yang Digunakan dalam Sampel

Model Pertama Kode berikut digunakan untuk sampel di halaman ini.

using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
// add a reference to System.ComponentModel.DataAnnotations DLL
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System;

public class SchoolEntities : DbContext
{
    public DbSet<Course> Courses { get; set; }
    public DbSet<Department> Departments { get; set; }
    public DbSet<Instructor> Instructors { get; set; }
    public DbSet<OfficeAssignment> OfficeAssignments { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // Configure Code First to ignore PluralizingTableName convention
        // If you keep this convention then the generated tables will have pluralized names.
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }
}

public class Department
{
    public Department()
    {
        this.Courses = new HashSet<Course>();
    }
    // Primary key
    public int DepartmentID { get; set; }
    public string Name { get; set; }
    public decimal Budget { get; set; }
    public System.DateTime StartDate { get; set; }
    public int? Administrator { get; set; }

    // Navigation property
    public virtual ICollection<Course> Courses { get; private set; }
}

public class Course
{
    public Course()
    {
        this.Instructors = new HashSet<Instructor>();
    }
    // Primary key
    public int CourseID { get; set; }

    public string Title { get; set; }
    public int Credits { get; set; }

    // Foreign key
    public int DepartmentID { get; set; }

    // Navigation properties
    public virtual Department Department { get; set; }
    public virtual ICollection<Instructor> Instructors { get; private set; }
}

public partial class OnlineCourse : Course
{
    public string URL { get; set; }
}

public partial class OnsiteCourse : Course
{
    public OnsiteCourse()
    {
        Details = new Details();
    }

    public Details Details { get; set; }
}

public class Details
{
    public System.DateTime Time { get; set; }
    public string Location { get; set; }
    public string Days { get; set; }
}

public class Instructor
{
    public Instructor()
    {
        this.Courses = new List<Course>();
    }

    // Primary key
    public int InstructorID { get; set; }
    public string LastName { get; set; }
    public string FirstName { get; set; }
    public System.DateTime HireDate { get; set; }

    // Navigation properties
    public virtual ICollection<Course> Courses { get; private set; }
}

public class OfficeAssignment
{
    // Specifying InstructorID as a primary
    [Key()]
    public Int32 InstructorID { get; set; }

    public string Location { get; set; }

    // When Entity Framework sees Timestamp attribute
    // it configures ConcurrencyCheck and DatabaseGeneratedPattern=Computed.
    [Timestamp]
    public Byte[] Timestamp { get; set; }

    // Navigation property
    public virtual Instructor Instructor { get; set; }
}