实体框架代码优先和火鸟 - 外键名称问题

本文关键字:问题 火鸟 代码 框架 实体 | 更新日期: 2023-09-27 18:37:23

我正在尝试使用 EF 代码优先和以下简单代码创建包含 2 个表(DistrictsDatabases)的新数据库:

using (var db = new FirebirdDBContext(_connectionString))
{
    db.Database.CreateIfNotExists();
}

我的课程:

public class District
{
    [Key]
    public int District_id { get; set; }
    [Column(TypeName = "VARCHAR")]
    [StringLength(50)]
    public string District_name { get; set; }
    [ForeignKey("DataBase")]
    public int DataBase_id { get; set; }
    public DataBase DataBase { get; set; }
}
public class DataBase
{
    [Key]
    public int DataBase_id { get; set; }
    [Column(TypeName = "VARCHAR")]
    [StringLength(50)]
    public string DataBase_name { get; set; }
    public ICollection<District> District { get; set; }
    public DataBase()
    {
        District = new List<District>();
    }
}    

但不幸的是,它抛出了一个错误:

指定的参数超出了有效值的范围。
参数名称:名称"FK_Districts_DataBases_DataBase_id"比 Firebird 对对象名称的 31 个字符限制长。

我知道 Firebird 的 31 个字符限制,但是如果我首先使用实体框架代码,我该如何解决这个问题?

实体框架代码优先和火鸟 - 外键名称问题

增加 Firebird 对元数据字段名称的 31 个字符限制一直是一个持续的功能请求,并且没有真正的方法来增加长度限制。

话虽如此,我们也许能够控制 EF 尝试生成的外键约束名称的长度......这将涉及将类属性重命名为较短的名称(但仍将它们映射到其真正的列名)

希望 EF 根据类的属性名称而不是实际列生成外键约束名称。

FK_Districts_DataBases_DataBase_id 是 23 + 11 = 34 个字符。我们需要让它少于 32 个字符。

我认为这个前缀可能是不可避免的。FK_Districts_DataBases_所以我们有7-8个字符后缀的余地。

试试这个:

public class District
{
    [Key]
    public int District_id { get; set; }
    [Column(TypeName = "VARCHAR")]
    [StringLength(50)]
    public string District_name { get; set; }
    [ForeignKey("DataBase")]
    [Column("DataBase_id")]
    public int DbId { get; set; } // reduce the column name
    public DataBase DataBase { get; set; }
}
public class DataBase
{
    [Key]
    [Column("DataBase_id")]
    public int DbId { get; set; } // reduce the column name
    [Column(TypeName = "VARCHAR")]
    [StringLength(50)]
    public string DataBase_name { get; set; }    
    public ICollection<District> District { get; set; }
    public DataBase()
    {
        District = new List<District>();
    }
} 

希望 EF 将约束名称设置为"FK_Districts_DataBases_DbId"(27 个字符)

添加迁移后,可以修改迁移类文件以更改外键名称,如下所示:

.PrimaryKey(t => t.ID)
            .ForeignKey("dbo.CONTESTS", t => t.CONTEST_ID, cascadeDelete: true, name:"FK_CONTESTS_ID")
            .Index(t => t.CONTEST_ID);

只需添加 name:"your_key",然后在 Down 方法中像这样对自动生成的字段名称出现 awoid 错误

DropForeignKey("dbo.BIBLIO_LIST","FK_CONTESTS_ID");

另一种解决方案是覆盖Firebird SQL生成器。

如果不想再次为 SQL 生成创建整个逻辑,可以创建一个派生自 FbMigrationSqlGenerator 的类,并重写所需的方法。

下面我使用一个简短的表名逻辑来创建外键名:

public class FirebirdSqlGenerator : FbMigrationSqlGenerator
{
    protected override IEnumerable<MigrationStatement> Generate(AddForeignKeyOperation operation)
    {
        // Reduce the name using this method
        operation.Name = GenerateForeignKeyNameFromOperation(operation);
        using (var writer = SqlWriter())
        {
            writer.Write("ALTER TABLE ");
            writer.Write(Quote(CheckName(ExtractName(operation.DependentTable))));
            writer.Write(" ADD CONSTRAINT ");
            writer.Write(Quote(CheckName(CreateItemName(operation.Name))));
            writer.Write(" FOREIGN KEY (");
            WriteColumns(writer, operation.DependentColumns.Select(Quote));
            writer.Write(") REFERENCES ");
            writer.Write(Quote(CheckName(ExtractName(operation.PrincipalTable))));
            writer.Write(" (");
            WriteColumns(writer, operation.PrincipalColumns.Select(Quote));
            writer.Write(")");
            if (operation.CascadeDelete)
            {
                writer.Write(" ON DELETE CASCADE");
            }
            yield return Statement(writer.ToString());
        }
    }
    public string GenerateForeignKeyNameFromOperation(AddForeignKeyOperation foreignKeyOperation)
    {
        var depTable = GetShortNameFromTableName(CreateItemName(foreignKeyOperation.DependentTable));
        foreignKeyOperation.Name = "FK_" +
                         depTable +
                         "_" +
                         GetShortNameFromTableName(CreateItemName(foreignKeyOperation.PrincipalTable)) +
                         "_" +
                         String.Join("_", foreignKeyOperation.DependentColumns);
        return foreignKeyOperation.Name;
    }
    [...]
}

将其设置为使用 SetSqlGenerator 方法的 SQL 生成器。它将看起来像这样:

internal sealed class MyConfiguration : DbMigrationsConfiguration<MyDbContext>
{
    private string firebirdProviderInvariantName = "FirebirdSql.Data.FirebirdClient";
    /// <summary>
    /// Initializes a new instance of the <see cref="Configuration"/> class.
    /// </summary>
    public MyConfiguration()
    {
        SetSqlGenerator(firebirdProviderInvariantName, new FirebirdSqlGenerator();
    }
}

额外提示:如果要调试代码以检查问题,可以在 Generate 方法中添加以下行:

        if (System.Diagnostics.Debugger.IsAttached == false)
            System.Diagnostics.Debugger.Launch();
        System.Diagnostics.Debugger.Break();

这将启动一个调试会话,你可以在Visual Studio的另一个实例中捕获该会话。

其他一些可能对您有所帮助的页面:火鸟回购:https://github.com/cincuranet/FirebirdSql.Data.FirebirdClientEF 存储库:https://github.com/aspnet/EntityFramework6

我希望它有所帮助。