Entity Framework 6 中的 ScriptIgnore 属性破坏了外键属性

问题描述

我们有一个使用 .NET Framework 4.5.2 和 Entity Framework 6.2.0 的现有代码优先数据库,其中模型在单独的类库中定义。

DAL 类库定义了从通用模型继承的自己的模型。

在 DAL 模型中,外键使用 ForeignKey 属性定义,因为有时无法应用认命名约定。

我们向导航属性添加ScriptIgnore 属性,以防止出现序列化问题。

一切都按预期进行,除了在添加新迁移时会删除“某些”外键,使用认命名约定添加新列,并将外键应用于这些新列。

只需删除 ScriptIgnore 属性,迁移就可以了,外键都保持原样。

并非所有外键都以这种方式删除和重新创建,例如在同一张表上有 3 个外键,它们都没有遵循命名约定,因此用 ForeignKeyScriptIgnore 修饰属性,但只有其中一个被迁移删除并重新创建。

我们无法弄清楚在哪种情况下 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 帖子并评论说您遇到了同样的问题。

在此期间,我有两个可能的建议:

  1. 将域实体对象与数据传输对象 (DTO) 分开。然后,使用 [ScriptIgnore] 注释 DTO 中的属性并序列化 DTO 而不是实体(或者只是不在 DTO 中包含该属性)。我强烈建议采用这种方法,即使您没有遇到过这个错误,因为我总是希望尽可能保持域对象的清洁(没有 DB 或序列化属性)。实体到 DTO 的映射可以很容易地手动完成,但我仍然建议使用流行的库 Automapper
  2. 保持原样,但无论何时创建新的迁移,请记住手动编辑脚手架代码以删除 EF 尝试执行的 FK 属性的错误删除/创建。