[英]EF Code First foreign key without navigation property, but with parent collection property
[英]EF Code First foreign key without navigation property
假設我有以下實體:
public class Parent
{
public int Id { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
}
什么是代碼優先的流暢 API 語法,以強制在數據庫中創建 ParentId,並使用對Parents 表的外鍵約束,而無需具有導航屬性?
我知道,如果我將導航屬性 Parent 添加到 Child,那么我可以這樣做:
modelBuilder.Entity<Child>()
.HasRequired<Parent>(c => c.Parent)
.WithMany()
.HasForeignKey(c => c.ParentId);
但是在這種特殊情況下我不想要導航屬性。
雖然這篇文章是針對Entity Framework
而不是Entity Framework Core
,但對於想要使用 Entity Framework Core(我使用的是 V1.1.2)來實現相同目標的人來說,它可能很有用。
我不需要導航屬性(雖然它們很好),因為我正在練習 DDD 並且我希望Parent
和Child
是兩個單獨的聚合根。 我希望他們能夠通過外鍵而不是通過特定於基礎結構的Entity Framework
導航屬性相互交談。
您所要做的就是使用HasOne
和WithMany
在一側配置關系,而不指定導航屬性(它們畢竟不存在)。
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");
});
......
}
}
我也給出了如何配置實體屬性的示例,但這里最重要的是HasOne<>
、 WithMany()
和HasForeignKey()
。
希望它有幫助。
使用 EF Code First Fluent API 是不可能的。 您始終需要至少一個導航屬性來在數據庫中創建外鍵約束。
如果您使用的是 Code First 遷移,您可以選擇在包管理器控制台上添加新的基於代碼的遷移 ( add-migration SomeNewSchemaName
)。 如果您對模型進行了更改或映射,則會添加新的遷移。 如果您沒有更改任何內容,請使用add-migration -IgnoreChanges SomeNewSchemaName
強制進行新遷移。 在這種情況下,遷移將只包含空的Up
和Down
方法。
然后,您可以通過向其添加以下內容來修改Up
方法:
public override void Up()
{
// other stuff...
AddForeignKey("ChildTableName", "ParentId", "ParentTableName", "Id",
cascadeDelete: true); // or false
CreateIndex("ChildTableName", "ParentId"); // if you want an index
}
運行此遷移(包管理控制台上的update-database
)將運行與此類似的 SQL 語句(對於 SQL Server):
ALTER TABLE [ChildTableName] ADD CONSTRAINT [FK_SomeName]
FOREIGN KEY ([ParentId]) REFERENCES [ParentTableName] ([Id])
CREATE INDEX [IX_SomeName] ON [ChildTableName] ([ParentId])
或者,如果沒有遷移,您可以使用以下命令運行純 SQL 命令
context.Database.ExecuteSqlCommand(sql);
其中context
是您派生的 context 類的實例,而sql
只是上述 SQL 命令作為字符串。
請注意,所有這些 EF 都不知道ParentId
是描述關系的外鍵。 EF 只會將其視為普通的標量屬性。 不知何故,與僅打開 SQL 管理工具並手動添加約束相比,上述所有方法只是一種更復雜和更慢的方法。
對於EF Core,您不一定需要提供導航屬性。 您可以簡單地在關系的一側提供一個外鍵。 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; }
}
}
對於那些想要使用 DataAnotations 並且不想公開導航屬性的人的小提示 - 使用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; }
}
就是這樣 - 將創建Add-Migration
后帶有cascade:true
的外鍵cascade:true
。
我正在使用 .Net Core 3.1、EntityFramework 3.1.3。 我一直在四處尋找,我想出的解決方案是使用HasForeginKey<DependantEntityType>(e => e.ForeginKeyProperty)
的通用版本。 您可以像這樣創建一對一的關系:
builder.entity<Parent>()
.HasOne<Child>()
.WithOne<>()
.HasForeginKey<Child>(c => c.ParentId);
builder.entity<Child>()
.Property(c => c.ParentId).IsRequired();
希望這有助於或至少提供一些關於如何使用HasForeginKey
方法的其他想法。
我不使用導航屬性的原因是類依賴性。 我將我的模型分成幾個組件,這些組件可以在不同項目中以任意組合使用或不使用。 因此,如果我的實體具有從另一個程序集進行分類的導航屬性,我需要引用該程序集,我想避免這種情況(或任何使用該完整數據模型的一部分的項目都將攜帶所有內容)。
我有單獨的遷移應用程序,用於遷移(我使用自動遷移)和初始數據庫創建。 該項目通過顯而易見的原因引用了所有內容。
解決方案是 C 風格的:
alt
鍵拖放)#if _MIGRATION
禁用導航屬性(和 FK 屬性)Contact
類的程序集)。樣品:
public int? ContactId { get; set; }
#if _MIGRATION
[ForeignKey(nameof(ContactId))]
public Contact Contact { get; set; }
#endif
當然,您應該以同樣的方式禁用using
指令並更改命名空間。
之后,所有使用者都可以像往常一樣使用該屬性作為 DB 字段(如果不需要,則不要引用其他程序集),但 DB 服務器將知道它是 FK 並且可以使用級聯。 很臟的解決方案。 但是有效。
請問從實體類中刪除導航屬性有什么好處? 那我們可以使用急切的加載表格嗎?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.