【问题标题】:Entity Framework Code First and Firebird - Foreign Key name issue实体框架代码优先和火鸟 - 外键名称问题
【发布时间】:2016-03-04 06:27:28
【问题描述】:

我正在尝试使用 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 个字符限制,但是如果我先使用实体​​框架代码,我该如何解决这个问题?

【问题讨论】:

标签: c# database entity-framework firebird


【解决方案1】:

增加 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 个字符)

【讨论】:

  • 我认为 Oracle 也会遇到同样的问题(限制为 30 个字符)。
  • 是的。我猜任何下游系统的限制都会适用。
  • @RajaNadar 非常感谢您的回答,非常感谢您为解决此问题提供的帮助。但我想补充一点——你在减少列名时犯了一个错误。为了使这段代码正常工作,我们应该减少注释中的列名而不是类中的字段名本身。
  • 感谢您指出错误@whizzzkey .. 所以您必须将数据库中的列重命名为更小的长度?
  • @RajaNadar 是的,为了更多的理解,我将描述 - 你写道: [Column("DataBase_id")] public int DbId { get;放; } ,我以这种方式进行了更改: [Column("DbId")] public int DataBase_id{ get;放; }
【解决方案2】:

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

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

只需添加名称:“your_key”,并在这样的 Down 方法中避免自动生成的字段名称错误

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

【讨论】:

    【解决方案3】:

    另一种解决方案是覆盖 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.FirebirdClient 英孚回购: https://github.com/aspnet/EntityFramework6

    希望对你有帮助。

    【讨论】:

    猜你喜欢
    • 2015-02-24
    • 1970-01-01
    • 2011-08-05
    • 1970-01-01
    • 2012-05-10
    • 2021-01-05
    • 2011-08-05
    相关资源
    最近更新 更多