Entity Framework Code First and Firebird - Foreign

2019-03-01 21:26发布

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?

3条回答
小情绪 Triste *
2楼-- · 2019-03-01 21:34

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");
查看更多
相关推荐>>
3楼-- · 2019-03-01 21:48

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)

查看更多
我命由我不由天
4楼-- · 2019-03-01 21:48

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.

查看更多
登录 后发表回答