Bagaimana Saya Harus Menyatakan Hubungan Kunci Asing Menggunakan Code First Entity Framework (4.1) di MVC3?

104

Saya telah mencari sumber daya tentang cara mendeklarasikan hubungan kunci asing dan kendala lainnya menggunakan kode EF 4.1 pertama tanpa banyak keberuntungan. Pada dasarnya saya membangun model data dalam kode dan menggunakan MVC3 untuk menanyakan model itu. Semuanya bekerja melalui MVC yang sangat bagus (pujian untuk Microsoft!) Tetapi sekarang saya ingin TIDAK berfungsi karena saya perlu memiliki batasan model data.

Misalnya, saya memiliki objek Order yang memiliki banyak properti yang merupakan objek eksternal (tabel). Saat ini saya dapat membuat Order tanpa masalah, tetapi tanpa dapat menambahkan kunci asing atau objek eksternal. MVC3 mengatur ini tidak masalah.

Saya menyadari bahwa saya hanya dapat menambahkan objek sendiri di kelas pengontrol sebelum menyimpan, tetapi saya ingin panggilan ke DbContext.SaveChanges () gagal jika hubungan batasan belum terpenuhi.

INFORMASI BARU

Jadi, secara khusus, saya ingin pengecualian terjadi ketika saya mencoba untuk menyimpan objek Order tanpa menentukan objek pelanggan. Ini tampaknya bukan perilaku jika saya hanya membuat objek seperti yang dijelaskan di sebagian besar dokumentasi EF Code First.

Kode terbaru:

public class Order
{
    public int Id { get; set; }

    [ForeignKey( "Parent" )]
    public Patient Patient { get; set; }

    [ForeignKey("CertificationPeriod")]
    public CertificationPeriod CertificationPeriod { get; set; }

    [ForeignKey("Agency")]
    public Agency Agency { get; set; }

    [ForeignKey("Diagnosis")]
    public Diagnosis PrimaryDiagnosis { get; set; }

    [ForeignKey("OrderApprovalStatus")]
    public OrderApprovalStatus ApprovalStatus { get; set; }

    [ForeignKey("User")]
    public User User { get; set; }

    [ForeignKey("User")]
    public User Submitter { get; set; }

    public DateTime ApprovalDate { get; set; }
    public DateTime SubmittedDate { get; set; }
    public Boolean IsDeprecated { get; set; }
}

Ini adalah kesalahan yang saya dapatkan sekarang saat mengakses tampilan VS yang dihasilkan untuk Pasien:

PESAN EROR

The ForeignKeyAttribute pada properti 'Patient' pada jenis 'PhysicianPortal.Models.Order' tidak valid. Nama kunci asing 'Parent' tidak ditemukan pada jenis dependen 'PhysicianPortal.Models.Order'. Nilai Nama harus berupa daftar nama properti kunci asing yang dipisahkan koma.

Salam,

Guido

Guido Anselmi
sumber

Jawaban:

164

Jika Anda memiliki sebuah Orderkelas, menambahkan properti yang mereferensikan kelas lain dalam model Anda, misalnya Customersudah cukup untuk memberi tahu EF ada hubungan di sana:

public class Order
{
    public int ID { get; set; }

    // Some other properties

    // Foreign key to customer
    public virtual Customer Customer { get; set; }
}

Anda selalu dapat menyetel FKrelasi secara eksplisit:

public class Order
{
    public int ID { get; set; }

    // Some other properties

    // Foreign key to customer
    [ForeignKey("Customer")]
    public string CustomerID { get; set; }
    public virtual Customer Customer { get; set; }
}

The ForeignKeyAttributekonstruktor mengambil string sebagai parameter: jika Anda menempatkannya pada properti kunci asing itu mewakili nama properti navigasi terkait. Jika Anda meletakkannya di properti navigasi, ini mewakili nama kunci asing terkait.

Artinya, jika Anda menempatkan di mana ForeignKeyAttributepada Customerproperti, atribut akan mengambil CustomerIDkonstruktor:

public string CustomerID { get; set; }
[ForeignKey("CustomerID")]
public virtual Customer Customer { get; set; }

EDIT berdasarkan Kode Terbaru Anda mendapatkan kesalahan itu karena baris ini:

[ForeignKey("Parent")]
public Patient Patient { get; set; }

EF akan mencari properti yang dipanggil Parentuntuk menggunakannya sebagai penegak Kunci Asing. Anda dapat melakukan 2 hal:

1) Hapus ForeignKeyAttributedan ganti dengan RequiredAttributeuntuk menandai relasi yang diperlukan:

[Required]
public virtual Patient Patient { get; set; }

Mendekorasi properti dengan RequiredAttributejuga memiliki efek samping yang bagus: Relasi dalam database dibuat dengan ON DELETE CASCADE.

Saya juga merekomendasikan membuat properti virtualuntuk mengaktifkan Lazy Loading.

2) Buat properti bernama Parentyang akan berfungsi sebagai Kunci Asing. Dalam hal ini mungkin lebih masuk akal untuk memanggilnya misalnya ParentID(Anda harus mengubah nama di ForeignKeyAttributejuga):

public int ParentID { get; set; }

Dalam pengalaman saya dalam kasus ini meskipun bekerja lebih baik untuk memiliki sebaliknya:

[ForeignKey("Patient")]
public int ParentID { get; set; }

public virtual Patient Patient { get; set; }
Sergi Papaseit
sumber
Terima kasih Sergi - Saya menambahkan beberapa informasi tambahan di kutipan blok.
Guido Anselmi
@Guido - Saya telah memperbarui jawaban saya berdasarkan edit kode terbaru Anda, semoga ini membantu.
Sergi Papaseit
30

Anda dapat mendefinisikan kunci asing dengan:

public class Parent
{
   public int Id { get; set; }
   public virtual ICollection<Child> Childs { get; set; }
}

public class Child
{
   public int Id { get; set; }
   // This will be recognized as FK by NavigationPropertyNameForeignKeyDiscoveryConvention
   public int ParentId { get; set; } 
   public virtual Parent Parent { get; set; }
}

Sekarang ParentId adalah properti kunci asing dan mendefinisikan hubungan yang diperlukan antara anak dan induk yang ada. Menyimpan anak tanpa keluar dari orang tua akan menimbulkan pengecualian.

Jika nama properti FK Anda tidak terdiri dari nama properti navigasi dan nama PK induk, Anda harus menggunakan anotasi data ForeignKeyAttribute atau fluent API untuk memetakan relasi

Anotasi data:

// The name of related navigation property
[ForeignKey("Parent")]
public int ParentId { get; set; }

API Lancar:

modelBuilder.Entity<Child>()
            .HasRequired(c => c.Parent)
            .WithMany(p => p.Childs)
            .HasForeignKey(c => c.ParentId);

Jenis kendala lainnya dapat diberlakukan dengan anotasi data dan validasi model .

Edit:

Anda akan mendapatkan pengecualian jika tidak disetel ParentId. Ini adalah properti wajib (bukan nullable). Jika Anda tidak mengaturnya, kemungkinan besar akan mencoba mengirim nilai default ke database. Nilai defaultnya adalah 0 jadi jika Anda tidak memiliki pelanggan dengan Id = 0 Anda akan mendapatkan pengecualian.

Ladislav Mrnka
sumber
Terima kasih Ladislav - Saya menambahkan beberapa informasi tambahan di kutipan blok.
Guido Anselmi
@Ladislav. Jadi untuk memberlakukan batasan ini, saya HARUS memiliki referensi ke Parent dan referensi ke ParentId. Apakah itu benar? Saya akan menambahkan kelas sebenarnya di atas untuk referensi.
Guido Anselmi
@Guido: Itu adalah informasi baru. Anda tidak menggunakan properti kunci asing. Semua properti navigasi Anda ditangani sebagai opsional secara default. Gunakan pemetaan fasih untuk memetakannya sesuai kebutuhan.
Ladislav Mrnka
@ Ladislav: Sekali lagi terima kasih. Saya melihat sekeliling untuk memahami perbedaan antara menggunakan Anotasi Data dan Fluent API. Saya membuat perubahan pada kode di atas sejalan dengan apa yang saya pikir Anda katakan. Apakah di atas semua yang harus saya lakukan? Salam.
Guido Anselmi
Tidak ada atribut ForeignKey yang mendefinisikan properti navigasi yang terkait dengan properti kunci asing atau sebaliknya. Anda tidak memiliki properti kunci asing sehingga Anda tidak dapat menggunakan atribut itu. Coba gunakan atribut Diperlukan pada properti navigasi Anda (saya tidak mengujinya).
Ladislav Mrnka