C# 实体框架代码优先和Firebird-外键名称问题

C# 实体框架代码优先和Firebird-外键名称问题,c#,database,entity-framework,firebird,C#,Database,Entity Framework,Firebird,我正在尝试首先使用EF代码和以下简单代码创建包含两个表(地区和数据库)的新数据库: using (var db = new FirebirdDBContext(_connectionString)) { db.Database.CreateIfNotExists(); } 我的班级: public class District { [Key] public int District_id { get; set; } [Column(TypeName = "VA

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

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>();
    }
}    
公共类地区
{
[关键]
公共int区_id{get;set;}
[列(TypeName=“VARCHAR”)]
[长度(50)]
公共字符串District_name{get;set;}
[外键(“数据库”)]
公共int数据库_id{get;set;}
公共数据库{get;set;}
}
公共类数据库
{
[关键]
公共int数据库_id{get;set;}
[列(TypeName=“VARCHAR”)]
[长度(50)]
公共字符串数据库_name{get;set;}
公共ICollection区{get;set;}
公共数据库()
{
地区=新列表();
}
}    
但不幸的是,它抛出了一个错误:

指定的参数超出了有效值的范围。
参数名称:“FK_地区\数据库\数据库\ id”的名称长度超过Firebird对对象名称的31个字符限制


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

提高Firebird对元数据字段名称的31个字符限制一直是一个固定的功能要求,没有真正的方法来提高长度限制

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

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

FK_地区_数据库_数据库_id为23+11=34个字符。 我们需要将其限制在32个字符以下

我认为这个前缀可能是不可避免的。FK_地区_数据库_ 所以我们有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>();
    }
} 
公共类地区
{
[关键]
公共int区_id{get;set;}
[列(TypeName=“VARCHAR”)]
[长度(50)]
公共字符串District_name{get;set;}
[外键(“数据库”)]
[列(“数据库id”)]
public int DbId{get;set;}//减少列名
公共数据库{get;set;}
}
公共类数据库
{
[关键]
[列(“数据库id”)]
public int DbId{get;set;}//减少列名
[列(TypeName=“VARCHAR”)]
[长度(50)]
公共字符串数据库_name{get;set;}
公共ICollection区{get;set;}
公共数据库()
{
地区=新列表();
}
} 

希望EF将约束名称设置为“FK\u Districts\u DataBases\u DbId”(27个字符)

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

.PrimaryKey(t => t.ID)
            .ForeignKey("dbo.CONTESTS", t => t.CONTEST_ID, cascadeDelete: true, name:"FK_CONTESTS_ID")
            .Index(t => t.CONTEST_ID);
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();
    }
}
只需添加名称:“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;
    }

    [...]
}
这将启动一个调试会话,您可以在另一个VisualStudio实例中捕获该会话

其他一些可能对您有所帮助的页面: 火鸟回购: EF回购:


我希望这会有所帮助。

您可以在配置非常规外键名部分()中配置外键名()@kienct89 thx,我会尝试一下。我假设Oracle也会遇到同样的问题(最多30个字符)。是的。我想任何下游系统的限制都适用。@RajaNadar GREAT THX对于您的回答,我非常感谢您帮助解决这个问题。但是我想补充一点,你在减少列名时犯了一个错误。为了使这段代码正常工作,我们应该减少注释中的列名,而不是类中的字段名本身。。因此,您必须将db中的列重命名为较小的长度?@RajaNadar是的,为了更好地理解,我将描述-您编写了:[column(“DataBase_id”)]public int DbId{get;set;},我这样做了更改:[column(“DbId”)]public int DataBase_id{get;set;}什么是SqlWriter?SqlWriter是StringWriter的扩展-在示例中,我使用了一个类似于Firebird在其源代码中使用的一个:
        if (System.Diagnostics.Debugger.IsAttached == false)
            System.Diagnostics.Debugger.Launch();
        System.Diagnostics.Debugger.Break();