Anotasi Data Pertama Kode
Catatan
EF4.1 Onwards Only - Fitur, API, dll. yang dibahas di halaman ini diperkenalkan dalam Entity Framework 4.1. Jika Anda menggunakan versi yang lebih lama, beberapa atau semua informasi ini tidak berlaku.
Konten di halaman ini diadaptasi dari artikel yang awalnya ditulis oleh Julie Lerman (<http://thedatafarm.com>).
Kode Kerangka Kerja Entitas Pertama memungkinkan Anda menggunakan kelas domain Anda sendiri untuk mewakili model yang diandalkan EF untuk melakukan kueri, pelacakan perubahan, dan memperbarui fungsi. Code First memanfaatkan pola pemrograman yang disebut sebagai 'konvensi atas konfigurasi.' Code First akan mengasumsikan bahwa kelas Anda mengikuti konvensi Kerangka Kerja Entitas, dan dalam hal ini, akan secara otomatis mengetahui cara melakukan pekerjaannya. Namun, jika kelas Anda tidak mengikuti konvensi tersebut, Anda memiliki kemampuan untuk menambahkan konfigurasi ke kelas Anda untuk memberikan informasi yang diperlukan kepada EF.
Code First memberi Anda dua cara untuk menambahkan konfigurasi ini ke kelas Anda. Salah satunya menggunakan atribut sederhana yang disebut DataAnnotations, dan yang kedua menggunakan API Fluent Code First, yang memberi Anda cara untuk menjelaskan konfigurasi secara imperatif, dalam kode.
Artikel ini akan berfokus pada penggunaan DataAnnotations (di namespace Layanan System.ComponentModel.DataAnnotations) untuk mengonfigurasi kelas Anda – menyoroti konfigurasi yang paling umum diperlukan. DataAnnotations juga dipahami oleh sejumlah aplikasi .NET, seperti ASP.NET MVC yang memungkinkan aplikasi ini memanfaatkan anotasi yang sama untuk validasi sisi klien.
Model
Saya akan menunjukkan Code First DataAnnotations dengan sepasang kelas sederhana: Blog dan Posting.
public class Blog
{
public int Id { get; set; }
public string Title { get; set; }
public string BloggerName { get; set;}
public virtual ICollection<Post> Posts { get; set; }
}
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public ICollection<Comment> Comments { get; set; }
}
Apa adanya, kelas Blog dan Post dengan mudah mengikuti konvensi pertama kode dan tidak memerlukan tweak untuk mengaktifkan kompatibilitas EF. Namun, Anda juga dapat menggunakan anotasi untuk memberikan informasi lebih lanjut kepada EF tentang kelas dan database yang mereka petakan.
Tombol
Entity Framework bergantung pada setiap entitas yang memiliki nilai kunci yang digunakan untuk pelacakan entitas. Salah satu konvensi Code First adalah properti kunci implisit; Code First akan mencari properti bernama "Id", atau kombinasi nama kelas dan "Id", seperti "BlogId". Properti ini akan memetakan ke kolom kunci utama dalam database.
Kelas Blog dan Post keduanya mengikuti konvensi ini. Bagaimana jika tidak? Bagaimana jika Blog menggunakan nama PrimaryTrackingKey sebagai gantinya, atau bahkan foo? Jika kode pertama-tama tidak menemukan properti yang cocok dengan konvensi ini, kode tersebut akan memberikan pengecualian karena persyaratan Entity Framework bahwa Anda harus memiliki properti kunci. Anda dapat menggunakan anotasi kunci untuk menentukan properti mana yang akan digunakan sebagai EntityKey.
public class Blog
{
[Key]
public int PrimaryTrackingKey { get; set; }
public string Title { get; set; }
public string BloggerName { get; set;}
public virtual ICollection<Post> Posts { get; set; }
}
Jika Anda menggunakan fitur pembuatan database kode pertama, tabel Blog akan memiliki kolom kunci utama bernama PrimaryTrackingKey, yang juga didefinisikan sebagai Identitas secara default.
Kunci komposit
Entity Framework mendukung kunci komposit - kunci primer yang terdiri dari lebih dari satu properti. Misalnya, Anda dapat memiliki kelas Paspor yang kunci utamanya adalah kombinasi dari PassportNumber dan IssuingCountry.
public class Passport
{
[Key]
public int PassportNumber { get; set; }
[Key]
public string IssuingCountry { get; set; }
public DateTime Issued { get; set; }
public DateTime Expires { get; set; }
}
Mencoba menggunakan kelas di atas dalam model EF Anda akan menghasilkan InvalidOperationException
:
Tidak dapat menentukan urutan kunci primer komposit untuk jenis 'Paspor'. Gunakan ColumnAttribute atau metode HasKey untuk menentukan urutan kunci primer komposit.
Untuk menggunakan kunci komposit, Kerangka Kerja Entitas mengharuskan Anda menentukan urutan properti kunci. Anda dapat melakukan ini dengan menggunakan anotasi Kolom untuk menentukan urutan.
Catatan
Nilai pesanan relatif (bukan berbasis indeks) sehingga nilai apa pun dapat digunakan. Misalnya, 100 dan 200 akan dapat diterima sebagai ganti 1 dan 2.
public class Passport
{
[Key]
[Column(Order=1)]
public int PassportNumber { get; set; }
[Key]
[Column(Order = 2)]
public string IssuingCountry { get; set; }
public DateTime Issued { get; set; }
public DateTime Expires { get; set; }
}
Jika Anda memiliki entitas dengan kunci asing komposit, maka Anda harus menentukan urutan kolom yang sama dengan yang Anda gunakan untuk properti kunci utama yang sesuai.
Hanya pengurutan relatif dalam properti kunci asing yang harus sama, nilai yang tepat yang ditetapkan ke Pesanan tidak perlu cocok. Misalnya, di kelas berikut, 3 dan 4 dapat digunakan sebagai ganti 1 dan 2.
public class PassportStamp
{
[Key]
public int StampId { get; set; }
public DateTime Stamped { get; set; }
public string StampingCountry { get; set; }
[ForeignKey("Passport")]
[Column(Order = 1)]
public int PassportNumber { get; set; }
[ForeignKey("Passport")]
[Column(Order = 2)]
public string IssuingCountry { get; set; }
public Passport Passport { get; set; }
}
Wajib
Anotasi Required
memberi tahu EF bahwa properti tertentu diperlukan.
Menambahkan Diperlukan ke properti Judul akan memaksa EF (dan MVC) untuk memastikan bahwa properti memiliki data di dalamnya.
[Required]
public string Title { get; set; }
Tanpa perubahan kode atau markup tambahan dalam aplikasi, aplikasi MVC akan melakukan validasi sisi klien, bahkan membangun pesan secara dinamis menggunakan properti dan nama anotasi.
Atribut Wajib juga akan memengaruhi database yang dihasilkan dengan membuat properti yang dipetakan tidak dapat diubah ke null. Perhatikan bahwa bidang Judul telah berubah menjadi "bukan 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.
MaxLength dan MinLength
Atribut MaxLength
dan MinLength
memungkinkan Anda menentukan validasi properti tambahan, seperti yang Anda lakukan dengan Required
.
Berikut adalah BloggerName dengan persyaratan panjang. Contoh ini juga menunjukkan cara menggabungkan atribut.
[MaxLength(10),MinLength(5)]
public string BloggerName { get; set; }
Anotasi MaxLength akan memengaruhi database dengan mengatur panjang properti menjadi 10.
Anotasi sisi klien MVC dan anotasi sisi server EF 4.1 akan menghormati validasi ini, sekali lagi secara dinamis membangun pesan kesalahan: "Bidang BloggerName harus berupa string atau jenis array dengan panjang maksimum '10'." Pesan itu agak panjang. Banyak anotasi memungkinkan Anda menentukan pesan kesalahan dengan atribut ErrorMessage.
[MaxLength(10, ErrorMessage="BloggerName must be 10 characters or less"),MinLength(5)]
public string BloggerName { get; set; }
Anda juga dapat menentukan ErrorMessage dalam anotasi diperlukan.
Tidak Dipetakan
Konvensi kode pertama menentukan bahwa setiap properti yang merupakan jenis data yang didukung diwakili dalam database. Tetapi ini tidak selalu terjadi dalam aplikasi Anda. Misalnya Anda mungkin memiliki properti di kelas Blog yang membuat kode berdasarkan bidang Judul dan BloggerName. Properti tersebut dapat dibuat secara dinamis dan tidak perlu disimpan. Anda dapat menandai properti apa pun yang tidak memetakan ke database dengan anotasi NotMapped seperti properti BlogCode ini.
[NotMapped]
public string BlogCode
{
get
{
return Title.Substring(0, 1) + ":" + BloggerName.Substring(0, 1);
}
}
Tipe Kompleks
Tidak jarang menggambarkan entitas domain Anda di sekumpulan kelas lalu melapisi kelas tersebut untuk menjelaskan entitas lengkap. Misalnya, Anda dapat menambahkan kelas yang disebut BlogDetails ke model Anda.
public class BlogDetails
{
public DateTime? DateCreated { get; set; }
[MaxLength(250)]
public string Description { get; set; }
}
Perhatikan bahwa BlogDetails
tidak memiliki jenis properti kunci apa pun. Dalam desain berbasis domain, BlogDetails
disebut sebagai objek nilai. Kerangka Kerja Entitas mengacu pada objek nilai sebagai jenis kompleks. Jenis kompleks tidak dapat dilacak sendiri.
Namun sebagai properti di Blog
kelas , BlogDetails
akan dilacak sebagai bagian Blog
dari objek. Agar kode terlebih dahulu mengenali ini, Anda harus menandai BlogDetails
kelas sebagai ComplexType
.
[ComplexType]
public class BlogDetails
{
public DateTime? DateCreated { get; set; }
[MaxLength(250)]
public string Description { get; set; }
}
Sekarang Anda dapat menambahkan properti di Blog
kelas untuk mewakili untuk blog tersebut BlogDetails
.
public BlogDetails BlogDetail { get; set; }
Dalam database, Blog
tabel akan berisi semua properti blog termasuk properti yang terkandung dalam propertinya BlogDetail
. Secara default, masing-masing didahului dengan nama jenis kompleks, "BlogDetail".
ConcurrencyCheck
Anotasi ConcurrencyCheck
memungkinkan Anda menandai satu atau beberapa properti yang akan digunakan untuk pemeriksaan konkurensi dalam database saat pengguna mengedit atau menghapus entitas. Jika Anda telah bekerja dengan Desainer EF, ini selaras dengan pengaturan properti ConcurrencyMode
ke Fixed
.
Mari kita lihat cara ConcurrencyCheck
kerjanya dengan menambahkannya ke BloggerName
properti .
[ConcurrencyCheck, MaxLength(10, ErrorMessage="BloggerName must be 10 characters or less"),MinLength(5)]
public string BloggerName { get; set; }
Ketika SaveChanges
dipanggil, karena ConcurrencyCheck
anotasi pada BloggerName
bidang, nilai asli properti tersebut akan digunakan dalam pembaruan. Perintah akan mencoba menemukan baris yang benar dengan memfilter tidak hanya pada nilai kunci tetapi juga pada nilai BloggerName
asli . Berikut adalah bagian penting dari perintah UPDATE yang dikirim ke database, di mana Anda dapat melihat perintah akan memperbarui baris yang memiliki PrimaryTrackingKey
adalah 1 dan BloggerName
"Julie" yang merupakan nilai asli ketika blog tersebut diambil dari database.
where (([PrimaryTrackingKey] = @4) and ([BloggerName] = @5))
@4=1,@5=N'Julie'
Jika seseorang telah mengubah nama blogger untuk blog tersebut sementara itu, pembaruan ini akan gagal dan Anda akan mendapatkan DbUpdateConcurrencyException yang perlu Anda tangani.
TimeStamp
Lebih umum menggunakan bidang rowversion atau tanda waktu untuk pemeriksaan konkurensi. Tetapi daripada menggunakan ConcurrencyCheck
anotasi, Anda dapat menggunakan anotasi yang lebih spesifik TimeStamp
selama jenis properti adalah array byte. Kode pertama-tama akan memperlakukan Timestamp
properti yang sama dengan ConcurrencyCheck
properti, tetapi juga akan memastikan bahwa bidang database yang pertama kali dihasilkan kode tidak dapat diubah ke null. Anda hanya dapat memiliki satu properti tanda waktu di kelas tertentu.
Menambahkan properti berikut ke kelas Blog:
[Timestamp]
public Byte[] TimeStamp { get; set; }
menghasilkan kode terlebih dahulu membuat kolom tanda waktu yang tidak dapat diubah ke null dalam tabel database.
Tabel dan Kolom
Jika Anda mengizinkan Code First membuat database, Anda mungkin ingin mengubah nama tabel dan kolom yang dibuatnya. Anda juga dapat menggunakan Code First dengan database yang sudah ada. Tetapi tidak selalu terjadi bahwa nama kelas dan properti di domain Anda cocok dengan nama tabel dan kolom di database Anda.
Kelas saya dinamai Blog
dan menurut konvensi, kode pertama-tama berasumsi ini akan memetakan ke tabel bernama Blogs
. Jika tidak demikian, Anda dapat menentukan nama tabel dengan Table
atribut . Di sini misalnya, anotasi menentukan bahwa nama tabel adalah InternalBlogs.
[Table("InternalBlogs")]
public class Blog
Anotasi Column
lebih mahir dalam menentukan atribut kolom yang dipetakan. Anda dapat menetapkan nama, jenis data, atau bahkan urutan kolom muncul dalam tabel. Berikut adalah contoh Column
atribut .
[Column("BlogDescription", TypeName="ntext")]
public String Description {get;set;}
Jangan bingung atribut Kolom TypeName
dengan DataType DataAnnotation. DataType adalah anotasi yang digunakan untuk UI dan diabaikan oleh Code First.
Berikut adalah tabel setelah diregenerasi. Nama tabel telah berubah menjadi InternalBlogs dan Description
kolom dari jenis kompleks sekarang BlogDescription
. Karena nama ditentukan dalam anotasi, kode terlebih dahulu tidak akan menggunakan konvensi memulai nama kolom dengan nama jenis kompleks.
DatabaseGenerated
Fitur database penting adalah kemampuan untuk memiliki properti komputasi. Jika Anda memetakan kelas Code First ke tabel yang berisi kolom komputasi, Anda tidak ingin Entity Framework mencoba memperbarui kolom tersebut. Tetapi Anda ingin EF mengembalikan nilai-nilai tersebut dari database setelah Anda menyisipkan atau memperbarui data. Anda dapat menggunakan DatabaseGenerated
anotasi untuk menandai properti tersebut di kelas Anda bersama dengan Computed
enum. Enum lainnya adalah None
dan Identity
.
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime DateCreated { get; set; }
Anda bisa menggunakan database yang dihasilkan pada kolom byte atau tanda waktu saat kode pertama kali menghasilkan database, jika tidak, Anda hanya boleh menggunakan ini saat menunjuk ke database yang ada karena kode terlebih dahulu tidak akan dapat menentukan rumus untuk kolom komputasi.
Anda membaca di atas bahwa secara default, properti kunci yang merupakan bilangan bulat akan menjadi kunci identitas dalam database. Itu akan sama dengan mengatur DatabaseGenerated
ke DatabaseGeneratedOption.Identity
. Jika Anda tidak ingin itu menjadi kunci identitas, Anda dapat mengatur nilainya ke DatabaseGeneratedOption.None
.
Indeks
Catatan
EF6.1 Onwards Only - Atribut Index
diperkenalkan dalam Entity Framework 6.1. Jika Anda menggunakan versi yang lebih lama, informasi di bagian ini tidak berlaku.
Anda dapat membuat indeks pada satu atau beberapa kolom menggunakan IndexAttribute. Menambahkan atribut ke satu atau beberapa properti akan menyebabkan EF membuat indeks yang sesuai dalam database saat membuat database, atau membuat perancah panggilan CreateIndex yang sesuai jika Anda menggunakan Migrasi Pertama Kode.
Misalnya, kode berikut akan mengakibatkan indeks dibuat pada Rating
kolom Posts
tabel dalam database.
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
[Index]
public int Rating { get; set; }
public int BlogId { get; set; }
}
Secara default, indeks akan diberi nama > IX_<property (IX_Rating dalam contoh di atas). Anda juga dapat menentukan nama untuk indeks. Contoh berikut menentukan bahwa indeks harus diberi nama PostRatingIndex
.
[Index("PostRatingIndex")]
public int Rating { get; set; }
Secara default, indeks tidak unik, tetapi Anda dapat menggunakan IsUnique
parameter bernama untuk menentukan bahwa indeks harus unik. Contoh berikut memperkenalkan indeks unik pada User
nama login.
public class User
{
public int UserId { get; set; }
[Index(IsUnique = true)]
[StringLength(200)]
public string Username { get; set; }
public string DisplayName { get; set; }
}
Indeks Beberapa Kolom
Indeks yang mencakup beberapa kolom ditentukan dengan menggunakan nama yang sama dalam beberapa anotasi Indeks untuk tabel tertentu. Saat membuat indeks multi-kolom, Anda perlu menentukan urutan untuk kolom dalam indeks. Misalnya, kode berikut membuat indeks multi-kolom pada Rating
dan BlogId
disebut IX_BlogIdAndRating. BlogId
adalah kolom pertama dalam indeks dan Rating
merupakan kolom kedua.
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
[Index("IX_BlogIdAndRating", 2)]
public int Rating { get; set; }
[Index("IX_BlogIdAndRating", 1)]
public int BlogId { get; set; }
}
Atribut Hubungan: InverseProperty dan ForeignKey
Catatan
Halaman ini menyediakan informasi tentang menyiapkan hubungan dalam model Kode Pertama Anda menggunakan Anotasi Data. Untuk informasi umum tentang hubungan di EF dan cara mengakses dan memanipulasi data menggunakan hubungan, lihat Hubungan & Properti Navigasi.*
Konvensi kode pertama akan mengurus hubungan yang paling umum dalam model Anda, tetapi ada beberapa kasus di mana ia membutuhkan bantuan.
Mengubah nama properti kunci di Blog
kelas membuat masalah dengan hubungannya menjadi Post
.
Saat membuat database, kode pertama-tama melihat BlogId
properti di kelas Post dan mengenalinya, dengan konvensi bahwa database cocok dengan nama kelas ditambah Id, sebagai kunci asing ke Blog
kelas. Tetapi tidak BlogId
ada properti di kelas blog. Solusi untuk ini adalah membuat properti navigasi di Post
dan menggunakan ForeignKey
DataAnnotation untuk membantu kode terlebih dahulu memahami cara membangun hubungan antara dua kelas (menggunakan Post.BlogId
properti) serta cara menentukan batasan dalam database.
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
[ForeignKey("BlogId")]
public Blog Blog { get; set; }
public ICollection<Comment> Comments { get; set; }
}
Batasan dalam database menunjukkan hubungan antara InternalBlogs.PrimaryTrackingKey
dan Posts.BlogId
.
InverseProperty
digunakan saat Anda memiliki beberapa hubungan antar kelas.
Post
Di kelas , Anda mungkin ingin melacak siapa yang menulis posting blog serta siapa yang mengeditnya. Berikut adalah dua properti navigasi baru untuk kelas Post.
public Person CreatedBy { get; set; }
public Person UpdatedBy { get; set; }
Anda juga perlu menambahkan kelas yang Person
dirujuk oleh properti ini. Kelas Person
memiliki properti navigasi kembali ke Post
, satu untuk semua postingan yang ditulis oleh orang tersebut dan satu untuk semua postingan yang diperbarui oleh orang tersebut.
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public List<Post> PostsWritten { get; set; }
public List<Post> PostsUpdated { get; set; }
}
Kode pertama tidak dapat mencocokkan properti di dua kelas sendiri. Tabel database untuk Posts
harus memiliki satu kunci asing untuk orang tersebut CreatedBy
dan satu untuk orang tersebut UpdatedBy
tetapi kode pertama-tama akan membuat empat properti kunci asing: Person_Id, Person_Id1, CreatedBy_Id dan UpdatedBy_Id.
Untuk memperbaiki masalah ini, Anda dapat menggunakan InverseProperty
anotasi untuk menentukan perataan properti.
[InverseProperty("CreatedBy")]
public List<Post> PostsWritten { get; set; }
[InverseProperty("UpdatedBy")]
public List<Post> PostsUpdated { get; set; }
PostsWritten
Karena properti di Person tahu bahwa ini mengacu pada Post
jenisnya, properti akan membangun hubungan ke Post.CreatedBy
. Demikian pula, PostsUpdated
akan terhubung ke Post.UpdatedBy
. Dan kode pertama tidak akan membuat kunci asing tambahan.
Ringkasan
DataAnnotations tidak hanya memungkinkan Anda menjelaskan validasi sisi klien dan server di kelas pertama kode Anda, tetapi juga memungkinkan Anda untuk meningkatkan dan bahkan memperbaiki asumsi yang pertama kali akan dibuat kode tentang kelas Anda berdasarkan konvensinya. Dengan DataAnnotations Anda tidak hanya dapat mendorong pembuatan skema database, tetapi Anda juga dapat memetakan kelas pertama kode Anda ke database yang sudah ada sebelumnya.
Meskipun sangat fleksibel, perlu diingat bahwa DataAnnotations hanya menyediakan perubahan konfigurasi yang paling umum diperlukan yang dapat Anda buat pada kelas pertama kode Anda. Untuk mengonfigurasi kelas Anda untuk beberapa kasus edge, Anda harus melihat mekanisme konfigurasi alternatif, API Fasih Code First .