假设我有以下实体:
public class Parent
{
public int Id { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
}
什么是代码第一个流畅的API语法,以强制在数据库中创建ParentId,并使用对Parent表的外键约束,而不需要具有导航属性?
我知道如果我将一个导航属性Parent添加到Child,那么我可以这样做:
modelBuilder.Entity<Child>()
.HasRequired<Parent>(c => c.Parent)
.WithMany()
.HasForeignKey(c => c.ParentId);
但我不希望在这种特殊情况下使用导航属性。
使用EF Code First Fluent API是不可能的。始终需要至少一个导航属性才能在数据库中创建外键约束。
如果您使用的是Code First Migrations,则可以选择在程序包管理器控制台(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
是派生上下文类的实例,sql
就是上面的SQL命令作为字符串。
请注意,尽管如此,EF并不知道ParentId
是描述关系的外键。 EF将其视为普通的标量属性。与仅打开SQL管理工具并手动添加约束相比,上述所有方法只是一种更复杂,更慢的方式。
虽然这篇文章是针对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()
。
希望能帮助到你。
那些想要使用DataAnnotations并且不想公开Navigation属性的人的小提示 - 使用qazxsw poi
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; }
}
之后创建cascade:true
的外键。
对于EF Core,您不一定需要提供导航属性。您只需在关系的一侧提供外键即可。 Fluent API的一个简单示例:
Add-Migration