问题描述
我一整天都在研究的奇怪问题。我正在使用实体框架 6。我的问题是我有三个实体:
public partial class Order : ILocationbearingObject
{
public int Id { get; set; }
// other properties and relationships here
public int? OrderProfileId { get; set; }
public int OrderTemplateId { get; set; }
public virtual OrderProfile Profile { get; set; } // optional property
public virtual OrderTemplate OrderTemplate{ get; set; }
}
public class OrderProfile
{
public int Id { get; set; }
// other properties
// added here 6/15/2021
public virtual OrderTemplate OrderTemplate{ get; set; }
}
public class OrderTemplate : EntityMetaData
{
public int Id { get; set; }
// other properties
public int? OrderProfileId{ get; set; }
public OrderProfile OrderProfile { get; set; }
}
在我们的模型构建器中,我们有以下定义:
modelBuilder.Entity<Order>()
.HasOptional(x => x.OrderProfile)
.WithMany(x => x.Orders)
.HasForeignKey(x => x.OrderProfileId);
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile);
但即使使用上述流畅的 api 模型,我们也会得到错误
无效的列名“OrderProfile_Id”
在各种测试中,我无法找到发生此问题的原因,因此我查看了我们的日志,发现当此错误开始出现时,它会抬头,然后能够找到与 OrderProfile 关联的更改,并发现唯一的更改所做的就是添加从 OrderProfile 到 OrderTemplate 的关系。
当我将那个流畅的 api 关系 OrderProfile 删除到 OrderTemplate 时,它按预期工作......我不需要与 OrderTemplate 的关系,但希望它在那里,我如何建立可选的 1 到可选的 1 关系不破坏其他关系?另外,为什么会受到其他关系的影响?
更新 6/15/2021 所以我发现我在 OrderProfile 模型中有一个反向导航属性:
public virtual OrderTemplate OrderTemplate{ get; set; }
删除那个和相关的流畅关系
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile);
执行上述操作解决了该问题,但由于某种原因,该问题似乎已下降到另一个具有上述循环引用的关系。 Order 类与这个级联问题有关。我想这是一个非常值得关注的问题,因为这个应用程序在过去的 4 年里运行良好,而这些关系像这样衰退是令人担忧的。有谁知道为什么会这样?
解决方法
如果您使用正确的命名约定,EF 将发挥神奇的作用。在此示例中,您不需要 fluent API 来关联实体。
public partial class Order : ILocationBearingObject
{
public int Id { get; set; }
public int? OrderProfileId { get; set; } //means HasOptional (nullable) and ForeignKey
//variable name must be OrderProfile not Profile
public virtual OrderProfile OrderProfile { get; set; }
}
public class OrderProfile
{
public OrderProfile()
{
Orders = new HashSet<Order>();
}
public int Id { get; set; }
//be aware circular reference at any conversion or mapping
public virtual ICollection<Order> Orders {get; set;} //means WithMany
}
,
我也遇到过这样的错误。这是由于 OrderTemplate 类中的 OrderProfileId 属性与流畅的 api 模型不匹配造成的
如果我没记错的话,您希望 OrderProfile 模型在 Order 和 OrderTemplate 之间建立多对多的关系。然后,如果是这种情况,请在 OrderProfile 中添加 nvaigation 属性。
public class OrderProfile
{
public int Id { get; set; }
// other properties
public virtual ICollection<Order> Orders { get; set; }
public virtual OrderTemplate OrderTemplate { get; set; }
}
然后把fluent api模型改成这样
// the EF has modelled the relation for normal 1 to many relation
// modelBuilder.Entity<Order>()
// .HasOptional(x => x.OrderProfile)
// .WithMany(x => x.Orders)
// .HasForeignKey(x => x.OrderProfileId);
modelBuilder.Entity<OrderTemplate>()
.HasOptional(x => x.OrderProfile)
.WithOptional(x => x.OrderTemplate);
,
您首先使用数据库,这总是会在实际数据库模型与 EF 从类和属性名称以及映射代码(= 概念模型)推断出的模型之间留下不匹配的空间。如果发生这种情况,让 EF 从概念模型生成数据库并查看它在哪里创建它期望的列 OrderProfile_Id
可能会有所帮助。
这是您在记录 SQL 语句时会看到的内容:
CREATE TABLE [dbo].[OrderTemplates] (
[Id] [int] NOT NULL IDENTITY,[OrderProfileId] [int],[OrderProfile_Id] [int],CONSTRAINT [PK_dbo.OrderTemplates] PRIMARY KEY ([Id])
)
...
ALTER TABLE [dbo].[OrderTemplates]
ADD CONSTRAINT [FK_dbo.OrderTemplates_dbo.OrderProfiles_OrderProfile_Id]
FOREIGN KEY ([OrderProfile_Id]) REFERENCES [dbo].[OrderProfiles] ([Id])
您会看到预期的可为空列 OrderProfile_Id
,它是 OrderProfiles
的 FK。值得注意的是,EF 不 使用 OrderProfileId
作为外键字段。它只是一个可以用于任何事情的字段。
那是因为 EF6 不支持 1:1 关联作为外键关联(引用属性和原始 FK 属性)。
知道了这一点,补救方法很简单:删除属性OrderTemplate.OrderProfileId
并告诉EF使用数据库中的字段OrderTemplate.OrderProfileId
:
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile)
.Map(m => m.MapKey("OrderProfileId"));
也就是说,我想知道为什么 Order
有一个指向 OrderProfile
的外键。它的 OrderProfile
不是由它的 OrderTemplate
决定的吗?如果是多余的关系,最好将其删除。