Katakanlah saya memiliki entitas berikut:
public class Parent
{
public int Id { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
}
Apa kode sintaks API pertama yang fasih untuk menegakkan ParentId yang dibuat dalam database dengan batasan kunci asing ke tabel Parents, tanpa perlu memiliki properti navigasi ?
Saya tahu bahwa jika saya menambahkan properti navigasi Parent to Child, maka saya bisa melakukan ini:
modelBuilder.Entity<Child>()
.HasRequired<Parent>(c => c.Parent)
.WithMany()
.HasForeignKey(c => c.ParentId);
Tetapi saya tidak ingin properti navigasi dalam kasus khusus ini.
entity-framework
ef-code-first
RationalGeek
sumber
sumber
Jawaban:
Dengan API EF Code First Fluent tidak mungkin. Anda selalu membutuhkan setidaknya satu properti navigasi untuk membuat batasan kunci asing dalam database.
Jika Anda menggunakan Migrasi Pertama Kode, Anda memiliki opsi untuk menambahkan migrasi berbasis kode baru di konsol pengelola paket (
add-migration SomeNewSchemaName
). Jika Anda mengubah sesuatu dengan model atau pemetaan, migrasi baru akan ditambahkan. Jika Anda tidak mengubah apa pun, paksa migrasi baru dengan menggunakanadd-migration -IgnoreChanges SomeNewSchemaName
. Migrasi hanya akan berisi metodeUp
dan kosongDown
dalam kasus ini.Kemudian Anda dapat memodifikasi
Up
metode dengan menambahkan follwing ke dalamnya:public override void Up() { // other stuff... AddForeignKey("ChildTableName", "ParentId", "ParentTableName", "Id", cascadeDelete: true); // or false CreateIndex("ChildTableName", "ParentId"); // if you want an index }
Menjalankan migrasi ini (
update-database
di konsol pengelolaan paket) akan menjalankan pernyataan SQL yang mirip dengan ini (untuk SQL Server):ALTER TABLE [ChildTableName] ADD CONSTRAINT [FK_SomeName] FOREIGN KEY ([ParentId]) REFERENCES [ParentTableName] ([Id]) CREATE INDEX [IX_SomeName] ON [ChildTableName] ([ParentId])
Atau, tanpa migrasi, Anda bisa menjalankan perintah SQL murni menggunakan
context.Database.ExecuteSqlCommand(sql);
di mana
context
adalah instance dari kelas konteks turunan Anda dansql
hanya perintah SQL di atas sebagai string.Sadarilah bahwa dengan semua ini EF tidak memiliki petunjuk yang
ParentId
merupakan kunci asing yang menggambarkan suatu hubungan. EF akan menganggapnya hanya sebagai properti skalar biasa. Entah bagaimana semua hal di atas hanyalah cara yang lebih rumit dan lebih lambat dibandingkan dengan hanya membuka alat manajemen SQL dan menambahkan batasan dengan tangan.sumber
[ForeignKey("ParentTableName")]
. Itu akan menghubungkan properti ke kunci apa pun yang ada di tabel induk. Sekarang Anda memiliki nama tabel hard-code.Meskipun posting ini
Entity Framework
bukan untukEntity Framework Core
, Mungkin berguna bagi seseorang yang ingin mencapai hal yang sama menggunakan Entity Framework Core (Saya menggunakan V1.1.2).Saya tidak perlu properti navigasi (meskipun mereka baik) karena saya berlatih DDD dan saya ingin
Parent
danChild
menjadi dua akar agregat terpisah. Saya ingin mereka dapat berbicara satu sama lain melalui kunci asing, bukan melaluiEntity Framework
properti navigasi khusus infrastruktur .Yang harus Anda lakukan adalah mengonfigurasi hubungan di satu sisi menggunakan
HasOne
danWithMany
tanpa menentukan properti navigasi (sama sekali tidak ada).public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {} protected override void OnModelCreating(ModelBuilder builder) { ...... builder.Entity<Parent>(b => { b.HasKey(p => p.Id); b.ToTable("Parent"); }); builder.Entity<Child>(b => { b.HasKey(c => c.Id); b.Property(c => c.ParentId).IsRequired(); // Without referencing navigation properties (they're not there anyway) b.HasOne<Parent>() // <--- .WithMany() // <--- .HasForeignKey(c => c.ParentId); // Just for comparison, with navigation properties defined, // (let's say you call it Parent in the Child class and Children // collection in Parent class), you might have to configure them // like: // b.HasOne(c => c.Parent) // .WithMany(p => p.Children) // .HasForeignKey(c => c.ParentId); b.ToTable("Child"); }); ...... } }
Saya memberikan contoh tentang cara mengonfigurasi properti entitas juga, tetapi yang paling penting di sini adalah
HasOne<>
,WithMany()
danHasForeignKey()
.Semoga membantu.
sumber
HasOne<Parent>()
dan.WithMany()
di konfigurasi anak mereka. Mereka tidak mereferensikan properti navigasi sama sekali, karena tidak ada properti navigasi yang ditentukan. Saya akan mencoba membuatnya lebih jelas dengan pembaruan saya.Petunjuk kecil bagi mereka, yang ingin menggunakan DataAnotations dan tidak ingin mengekspos Properti Navigasi - gunakan
protected
public class Parent { public int Id { get; set; } } public class Child { public int Id { get; set; } public int ParentId { get; set; } protected virtual Parent Parent { get; set; } }
Itu saja - kunci asing dengan
cascade:true
setelahAdd-Migration
akan dibuat.sumber
Parent
atau hanyaParentId
?virtual
Properti @arcWke tidak bolehprivate
.Dalam kasus EF Core Anda tidak perlu menyediakan properti navigasi. Anda cukup memberikan Kunci Asing di satu sisi hubungan. Contoh sederhana dengan Fluent API:
using Microsoft.EntityFrameworkCore; using System.Collections.Generic; namespace EFModeling.Configuring.FluentAPI.Samples.Relationships.NoNavigation { class MyContext : DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Post>() .HasOne<Blog>() .WithMany() .HasForeignKey(p => p.BlogId); } } public class Blog { public int BlogId { get; set; } public string Url { get; set; } } public class Post { public int PostId { get; set; } public string Title { get; set; } public string Content { get; set; } public int BlogId { get; set; } } }
sumber
Saya menggunakan .Net Core 3.1, EntityFramework 3.1.3. Saya telah mencari-cari dan Solusi yang saya temukan menggunakan versi generik
HasForeginKey<DependantEntityType>(e => e.ForeginKeyProperty)
. Anda dapat membuat relasi satu ke satu seperti ini:builder.entity<Parent>() .HasOne<Child>() .WithOne<>() .HasForeginKey<Child>(c => c.ParentId); builder.entity<Child>() .Property(c => c.ParentId).IsRequired();
Semoga ini bisa membantu atau setidaknya memberikan beberapa ide lain tentang cara menggunakan
HasForeginKey
metode ini.sumber
Alasan saya tidak menggunakan properti navigasi adalah ketergantungan kelas. Saya memisahkan model saya menjadi beberapa rakitan, yang dapat digunakan atau tidak digunakan dalam proyek yang berbeda dalam kombinasi apa pun. Jadi jika saya memiliki entitas yang memiliki properti nagivation ke kelas dari rakitan lain, saya perlu merujuk rakitan itu, yang ingin saya hindari (atau proyek apa pun yang menggunakan bagian dari model data lengkap itu akan membawa semuanya).
Dan saya memiliki aplikasi migrasi terpisah, yang digunakan untuk migrasi (saya menggunakan otomatisasi) dan pembuatan DB awal. Proyek ini mereferensikan semuanya dengan alasan yang jelas.
Solusinya adalah gaya-C:
alt
kunci di VS)#if _MIGRATION
Contact
class sebagai contoh).Sampel:
public int? ContactId { get; set; } #if _MIGRATION [ForeignKey(nameof(ContactId))] public Contact Contact { get; set; } #endif
Tentu saja Anda harus cara yang sama menonaktifkan
using
direktif dan mengubah namespace.Setelah itu semua konsumen dapat menggunakan properti tersebut seperti kolom DB biasa (dan tidak mereferensikan rakitan tambahan jika tidak diperlukan), tetapi server DB akan mengetahui bahwa itu adalah FK dan dapat menggunakan cascading. Solusi yang sangat kotor. Tapi berhasil.
sumber