能否在EF Core中通过字符串关联松散关联的实体?
基于Name字段配置Person与BannedName的可选关联
领域模型定义
我们拥有Person(人员)和BannedName(禁用名称)两个实体,均存在于DbContext中,代码如下:
class Person { public int Id { get; set; } public string Name { get; set; } // 可选关联 public BannedName BannedName { get; set; } } class BannedName { public int Id { get; set; } public string Name { get; set; } } public class GameDbContext : DbContext { public DbSet<Person> People { get; set; } public DbSet<BannedName> BannedNames { get; set; } }
示例数据
Person数据
| Id | Name |
|---|---|
| 1 | Bill |
| 2 | Rodent |
BannedName数据
| Id | Name |
|---|---|
| 1 | Rodent |
| 2 | Clown |
需求与期望
是否可以基于Name字段配置两者间的可选关联?期望执行以下查询代码时:
var personWithBannedName = _dbContext.People .Include(person => person.BannedName) .Single(person => person.Name == "Rodent");
能返回如下对象结构:
{ Id: 2, Name: "Rodent", BannedName: { Id: 1, Name: "Rodent" } }
选择该方案的理由
- 若在
Person中存储IsNameBanned布尔值,需随名称变更同步状态,本数据模型无需维护额外状态; - 使用
GroupJoin不利于领域测试和领域类的派生属性,我们希望脱离EF Core进行模型与测试设置,示例代码如下:
var personWithBannedName = new Person { Id = 1, Name = "Some Banned Name", BannedName = new BannedName { Id = 1, Name = "Some Banned Name" } }; Assert.True(personWithBannedName.IsNameBanned);
- 我们有大量基于内存SQLite的单元测试,希望避免使用SQL Server特定方案(如视图技巧)。
解决方案
可以实现该需求,通过EF Core的Fluent API配置基于Name字段的可选关联即可。在GameDbContext的OnModelCreating方法中添加以下配置:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Person>() .HasOne(p => p.BannedName) .WithMany() // BannedName无需定义反向导航属性 .HasForeignKey(p => p.Name) // 以Person的Name作为外键 .HasPrincipalKey(bn => bn.Name) // 以BannedName的Name作为替代键 .IsRequired(false); // 配置为可选关联 }
说明
- 该配置将
Person.Name与BannedName.Name建立关联,当Person的Name存在于BannedName列表中时,会自动关联对应的BannedName实体; - 配置兼容SQLite等内存数据库,无需依赖SQL Server特定功能;
- 领域模型可脱离EF Core独立使用,满足单元测试需求,直接构造实体即可验证派生属性(如
IsNameBanned)。
内容的提问来源于stack exchange,提问作者Ned Grady
相关产品推荐
相关产品推荐

