问题描述
我们有一个使用 .NET Framework 4.5.2 和 Entity Framework 6.2.0 的现有代码优先数据库,其中模型在单独的类库中定义。
DAL 类库定义了从通用模型继承的自己的模型。
在 DAL 模型中,外键使用 ForeignKey
属性定义,因为有时无法应用默认命名约定。
我们向导航属性添加了 ScriptIgnore
属性,以防止出现序列化问题。
一切都按预期进行,除了在添加新迁移时会删除“某些”外键,使用默认命名约定添加新列,并将外键应用于这些新列。
只需删除 ScriptIgnore
属性,迁移就可以了,外键都保持原样。
并非所有外键都以这种方式删除和重新创建,例如在同一张表上有 3 个外键,它们都没有遵循命名约定,因此用 ForeignKey
和 ScriptIgnore
修饰属性,但只有其中一个被迁移删除并重新创建。
我们无法弄清楚在哪种情况下 ForeignKey
属性不被尊重以及导致这种行为的原因。
public class Alarm
{
// ...
public int? idChannel { get; set; }
public int? idDevice { get; set; }
public int? idScenario { get; set; }
// ...
}
public class EFAlarm : Alarm
{
[ForeignKey("idChannel"),ScriptIgnore]
public virtual EFChannel Channel { get; set; } // This one is kept as is
[ForeignKey("idDevice"),ScriptIgnore]
public virtual EFDevice Device{ get; set; } // This one is kept as is
[ForeignKey("idScenario"),ScriptIgnore]
public virtual EFScenario Scenario { get; set; } // This one is dropped and recreated
}
这里是迁移的摘录:
public override void Up()
{
// ...
DropForeignKey("dbo.fe_alarms","idScenario","dbo.scenario");
DropIndex("dbo.fe_alarms",new[] { "idScenario" });
AddColumn("dbo.fe_alarms","Scenario_id",c => c.Int());
CreateIndex("dbo.fe_alarms","Scenario_id");
AddForeignKey("dbo.fe_alarms","dbo.scenario","id");
// ...
}
ScriptIgnore 属性在 System.Web 程序集中定义。 我们尝试使用在不同程序集中定义的其他自定义属性,但似乎没有一个会导致此类问题。 这种行为的原因可能是什么?
解决方法
经过一番研究,似乎是 2018 年 4 月已经报告过的 known issue。将 [ScriptIgnore]
添加到您的模型中有时会在您的迁移中产生问题。
不幸的是,从那时起似乎没有人关注它,问题仍然存在。您可能可以关注 Github 帖子并评论说您遇到了同样的问题。
在此期间,我有两个可能的建议:
- 将域实体对象与数据传输对象 (DTO) 分开。然后,使用
[ScriptIgnore]
注释 DTO 中的属性并序列化 DTO 而不是实体(或者只是不在 DTO 中包含该属性)。我强烈建议采用这种方法,即使您没有遇到过这个错误,因为我总是希望尽可能保持域对象的清洁(没有 DB 或序列化属性)。实体到 DTO 的映射可以很容易地手动完成,但我仍然建议使用流行的库Automapper
。 - 保持原样,但无论何时创建新的迁移,请记住手动编辑脚手架代码以删除 EF 尝试执行的 FK 属性的错误删除/创建。