如何解决EF Core 一对一双向自引用表
我在使用 EF Core 时遇到了一些问题。
我收到此错误:
InvalidOperationException:无法保存更改,因为在要保存的数据中检测到循环依赖
这是我的模型:
public class Transaction
{
public int TransactionId { get; set; }
public int? MutualTransactionId { get; set; }
public virtual Transaction MutualTransaction { get; set; }
public long Debit { get; set; }
public long Credit { get; set; }
}
这是我的 OnModelCreating
配置:
public class Repository : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Transaction>(entity =>
{
entity.HasKey(x => x.TransactionId);
entity.HasOne(x => x.MutualTransaction)
.WithOne()
.HasForeignKey<Transaction>(x => x.MutualTransactionId)
.OnDelete(DeleteBehavior.NoAction)
.HasConstraintName("FK_Transactions_Mutuals");
});
}
}
当我想将一系列数据植入数据库时会出现问题。
错误本身并不重要。我只想知道如何使用EF Core制作一对一的双向自引用表。
每一行都引用同一个表中的另一行,并且被引用的行也使用模型类中的相同属性引用回该行。
解决方法
你可以试试这可能有效:
public class Transaction
{
public int TransactionId { get; set; }
[ForeignKey("MutualTransaction"),Column(Order = 0)]
public int? MutualTransactionId { get; set; }
public long Debit { get; set; }
public long Credit { get; set; }
public virtual Transaction MutualTransaction { get; set; }
}
,
您所说的“一对一”关系实际上并不是“一对一”关系,而是一种自我引用的“多对一”关系。
对于这种关系,您不需要显式映射,一个简单的属性映射就可以了,但是如果您想要显式映射 /w modelBuilder:
modelBuilder.Entity<Transaction>(entity =>
{
entity.HasKey(x => x.TransactionId);
entity.HasOne(x => x.MutualTransaction)
.WithMany()
.HasForeignKey(x => x.MutualTransactionId)
.OnDelete(DeleteBehavior.NoAction)
.HasConstraintName("FK_Transactions_Mutuals");
});
一对一关系存在于通常共享相同 PK 的两个不同表之间。例如,如果一个事务有一些你通常不需要的大二进制列,并且在检索事务时不希望加载开销。
这是典型的自引用父样式多对一关系。如果您想确保一个实体仅被一个其他实体(1 个子实体)引用,并且该子实体只能拥有 1 个父实体,那么您要么必须通过应用程序逻辑强制执行该操作,要么采用连接多对多表在组成复合 PK 的每个键上添加了唯一约束。
更新:如果您希望 MutualTransaction 引用共同依赖项,则这是允许的,但不会强制执行。例如,如果我希望事务 A 和 B 相互依赖,其中 A 的互斥是 B,B 是 A,那么您可以使用上述映射和多对一来完成此操作,但这是一个隐含规则,具有由应用程序(而不是数据库)强制执行,因此您必须依靠代码和可能的数据库脚本运行状况检查来规范它的执行。问题是它需要 2 次显式保存:
var transactionA = new Transaction{ Name = "A" };
var transactionB = new Transaction{ Name = "B",MutualTransaction = transactionA };
context.Transactions.Add(transactionA);
context.Transactions.Add(transactionB);
context.SaveChanges();
transactionA.MutualTransaction = transactionB;
context.SaveChanges();
现在您可以加载任一实体并访问对共同合作伙伴的引用。
即
var test = context.Transactions
.Include(x => x.MutualTransaction)
.Single(x => x.Name == "A");
// or
var test2 = context.Transactions
.Include(x => x.MutualTransaction)
.Single(x => x.Name == "B");
如果这能奏效就好了:
var transactionA = new Transaction{ Name = "A" };
var transactionB = new Transaction{ Name = "B",MutualTransaction = transactionA };
transactionA.MutualTransaction = transactionB;
context.Transactions.Add(transactionA);
context.Transactions.Add(transactionB);
context.SaveChanges();
但这会导致 EF 中出现循环引用问题。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。