Powiedzmy, że mam następujące jednostki:
public class Parent
{
public int Id { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
}
Jaka jest pierwsza płynna składnia interfejsu API w kodzie, która wymusza utworzenie w bazie danych ParentId z ograniczeniem klucza obcego do tabeli Parents, bez konieczności posiadania właściwości nawigacji ?
Wiem, że jeśli dodam właściwość nawigacji Parent to Child, mogę to zrobić:
modelBuilder.Entity<Child>()
.HasRequired<Parent>(c => c.Parent)
.WithMany()
.HasForeignKey(c => c.ParentId);
Ale nie chcę, aby w tym konkretnym przypadku właściwość nawigacji.
entity-framework
ef-code-first
RationalGeek
źródło
źródło
Odpowiedzi:
Z EF Code First Fluent API jest to niemożliwe. Aby utworzyć ograniczenie klucza obcego w bazie danych, zawsze potrzebujesz co najmniej jednej właściwości nawigacji.
Jeśli korzystasz z migracji Code First, możesz dodać nową migrację opartą na kodzie w konsoli menedżera pakietów (
add-migration SomeNewSchemaName
). Jeśli coś zmieniłeś w swoim modelu lub mapowaniu, zostanie dodana nowa migracja. Jeśli nic nie zmieniłeś, wymuś nową migrację za pomocąadd-migration -IgnoreChanges SomeNewSchemaName
. W tym przypadku migracja będzie zawierać tylko pusteUp
iDown
metody.Następnie możesz zmodyfikować
Up
metodę, dodając do niej następującą:public override void Up() { // other stuff... AddForeignKey("ChildTableName", "ParentId", "ParentTableName", "Id", cascadeDelete: true); // or false CreateIndex("ChildTableName", "ParentId"); // if you want an index }
Uruchomienie tej migracji (
update-database
w konsoli zarządzania pakietami) spowoduje uruchomienie instrukcji SQL podobnej do poniższej (dla SQL Server):ALTER TABLE [ChildTableName] ADD CONSTRAINT [FK_SomeName] FOREIGN KEY ([ParentId]) REFERENCES [ParentTableName] ([Id]) CREATE INDEX [IX_SomeName] ON [ChildTableName] ([ParentId])
Alternatywnie, bez migracji, możesz po prostu uruchomić czyste polecenie SQL za pomocą
context.Database.ExecuteSqlCommand(sql);
gdzie
context
jest instancją Twojej pochodnej klasy kontekstu isql
jest po prostu powyższym poleceniem SQL jako ciągiem znaków.Należy pamiętać, że z tym wszystkim EF nie ma pojęcia, że
ParentId
jest to klucz obcy opisujący relację. EF uzna to tylko za zwykłą właściwość skalarną. W jakiś sposób wszystko powyższe jest tylko bardziej skomplikowanym i wolniejszym sposobem w porównaniu do zwykłego otwarcia narzędzia do zarządzania SQL i ręcznego dodania ograniczenia.źródło
[ForeignKey("ParentTableName")]
. To połączy właściwość z dowolnym kluczem w tabeli nadrzędnej. Teraz masz jednak zakodowaną na stałe nazwę tabeli.Chociaż ten post jest dla
Entity Framework
nieEntity Framework Core
, może być przydatny dla kogoś, kto chce osiągnąć to samo za pomocą Entity Framework Core (używam wersji 1.1.2).Nie muszę właściwości nawigacyjnych (chociaż są one ładne), ponieważ ćwiczę DDD i chcę
Parent
iChild
być dwa odrębne korzenie kruszywa. Chcę, aby mogli rozmawiać ze sobą za pomocą klucza obcego, a nie przezEntity Framework
właściwości nawigacji specyficzne dla infrastruktury .Wszystko, co musisz zrobić, to skonfigurować relację po jednej stronie, używając
HasOne
iWithMany
bez określania właściwości nawigacji (w końcu ich tam nie ma).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"); }); ...... } }
Daję z przykładów, w jaki sposób skonfigurować właściwości podmiotu, jak również, ale najważniejszy jest tutaj
HasOne<>
,WithMany()
iHasForeignKey()
.Mam nadzieję, że to pomoże.
źródło
HasOne<Parent>()
i.WithMany()
inną. W ogóle nie odwołują się do właściwości nawigacji, ponieważ i tak nie są zdefiniowane żadne właściwości nawigacji. Postaram się, aby było to jaśniejsze dzięki moim aktualizacjom.Mała wskazówka dla tych, którzy chcą korzystać z DataAnotations i nie chcą ujawniać właściwości Navigation Property - użyj
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; } }
To wszystko - zostanie utworzony klucz obcy z
cascade:true
afterAdd-Migration
.źródło
Parent
czy po prostuParentId
?virtual
Właściwości @MarcWittke nie mogą byćprivate
.W przypadku EF Core nie musisz koniecznie podawać właściwości nawigacji. Możesz po prostu podać klucz obcy po jednej stronie relacji. Prosty przykład z 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; } } }
źródło
Używam .Net Core 3.1, EntityFramework 3.1.3. Szukałem wokół, a rozwiązanie, które wymyśliłem, polegało na użyciu ogólnej wersji
HasForeginKey<DependantEntityType>(e => e.ForeginKeyProperty)
. możesz stworzyć relację jeden do jednego w następujący sposób:builder.entity<Parent>() .HasOne<Child>() .WithOne<>() .HasForeginKey<Child>(c => c.ParentId); builder.entity<Child>() .Property(c => c.ParentId).IsRequired();
Mam nadzieję, że to pomoże lub przynajmniej zapewni kilka innych pomysłów, jak używać tej
HasForeginKey
metody.źródło
Powodem, dla którego nie używam właściwości nawigacji, są zależności klas. Rozdzieliłem moje modele na kilka złożeń, które mogą być używane lub nie używane w różnych projektach w dowolnych kombinacjach. Więc jeśli mam jednostkę, która ma właściwość nagivation do klasy z innego zestawu, muszę odwołać się do tego zestawu, którego chcę uniknąć (lub jakikolwiek projekt, który używa części tego kompletnego modelu danych, będzie nosił wszystko ze sobą).
Mam oddzielną aplikację do migracji, która jest używana do migracji (używam automigracji) i początkowego tworzenia bazy danych. Ten projekt odnosi się do wszystkiego z oczywistych powodów.
Rozwiązanie jest w stylu C:
alt
kluczem w VS)#if _MIGRATION
Contact
klasą w przykładzie).Próba:
public int? ContactId { get; set; } #if _MIGRATION [ForeignKey(nameof(ContactId))] public Contact Contact { get; set; } #endif
Oczywiście w ten sam sposób należy wyłączyć
using
dyrektywy i zmienić przestrzeń nazw.Następnie wszyscy konsumenci mogą używać tej właściwości jako zwykłego pola bazy danych (i nie odwoływać się do dodatkowych zestawów, jeśli nie są potrzebne), ale serwer DB będzie wiedział, że jest to FK i może używać kaskadowania. Bardzo brudny roztwór. Ale działa.
źródło