简体   繁体   中英

Entity Framework Code First and Firebird - Foreign Key name issue

I'm trying to create new database with 2 tables ( Districts and Databases ) using EF code-first and this simple code:

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

My classes:

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>();
    }
}    

But unfortunately it throws an error:

Specified argument was out of the range of valid values.
Parameter name: The name 'FK_Districts_DataBases_DataBase_id' is longer than Firebird's 31 characters limit for object names.

I know about Firebird's 31 characters limit but how I can solve this problem if I use Entity Framework code-first?

Increasing Firebird's 31 character limit for metadata field names has been a constant feature request, and there is no real way to increase the length limit.

Having said that, we might be able to control the length of the foreign-key constraint name, that EF is trying to generate.. this will involve renaming your class properties to shorter names (but still map them to their true column names)

Hopefully EF generates the foreign-key constraint names off the class' property names, and not the actual columns.

FK_Districts_DataBases_DataBase_id is 23 + 11 = 34 characters. we need to get it under 32 characters..

i think this prefix maybe unavoidable. FK_Districts_DataBases_ so we have leeway for an 7-8 character suffix.

Try this:

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>();
    }
} 

hopefully EF makes the constraint name as 'FK_Districts_DataBases_DbId' (27 characters)

After you've added migration you can modify your migration class file to change foreign key name like this:

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

Just add name:"your_key", and in Down method like this to awoid errors for autogenerated field name

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

Another solution would be to override the Firebird SQL generator.

If you don't want to create the whole logic for the SQL generation again, you can create a class that derives from FbMigrationSqlGenerator and just override the methods you want.

Below I'm using a short table name logic to create the foreign key name:

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;
    }

    [...]
}

set it as you SQL Generator with the SetSqlGenerator method. It will look like this:

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();
    }
}

Extra hint: If you want to debug your code to check for problems you can add the following lines in your Generate method:

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

This will launch a Debug session that you can catch in another instance of Visual Studio.

Some other pages that may help you: Firebird Repo: https://github.com/cincuranet/FirebirdSql.Data.FirebirdClient EF Repo: https://github.com/aspnet/EntityFramework6

I hope it helps.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM