Entity Framework Code First 和 Firebird - 外键名称问题
Entity Framework Code First and Firebird - Foreign Key name issue
我正在尝试使用 EF 代码优先和以下简单代码创建包含 2 个表(Districts
和 Databases
)的新数据库:
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>();
}
}
但不幸的是它抛出一个错误:
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.
我知道 Firebird 的 31 个字符限制,但如果我使用 Entity Framework 代码优先,我该如何解决这个问题?
增加 Firebird 的元数据字段名称的 31 个字符限制一直是一个不变的功能请求,并且没有增加长度限制的真正方法。
话虽如此,我们也许能够控制 EF 试图生成的外键约束名称的长度。这将涉及将您的 class 属性重命名为较短的名称(但仍然将它们映射到它们的真实列名)
希望 EF 根据 class' 属性 名称而不是实际列生成外键约束名称。
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 个字符)
添加迁移后,您可以修改迁移 class 文件以更改外键名称,如下所示:
.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");
另一种解决方案是覆盖 Firebird SQL 生成器。
如果您不想再次为 SQL 代创建整个逻辑,您可以创建一个派生自 FbMigrationSqlGenerator 的 class 并只覆盖您想要的方法。
下面我使用短 table 名称逻辑来创建外键名称:
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();
}
}
额外提示:如果您想调试代码以检查问题,您可以在生成方法中添加以下行:
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
希望对您有所帮助。
我正在尝试使用 EF 代码优先和以下简单代码创建包含 2 个表(Districts
和 Databases
)的新数据库:
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>();
}
}
但不幸的是它抛出一个错误:
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.
我知道 Firebird 的 31 个字符限制,但如果我使用 Entity Framework 代码优先,我该如何解决这个问题?
增加 Firebird 的元数据字段名称的 31 个字符限制一直是一个不变的功能请求,并且没有增加长度限制的真正方法。
话虽如此,我们也许能够控制 EF 试图生成的外键约束名称的长度。这将涉及将您的 class 属性重命名为较短的名称(但仍然将它们映射到它们的真实列名)
希望 EF 根据 class' 属性 名称而不是实际列生成外键约束名称。
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 个字符)
添加迁移后,您可以修改迁移 class 文件以更改外键名称,如下所示:
.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");
另一种解决方案是覆盖 Firebird SQL 生成器。
如果您不想再次为 SQL 代创建整个逻辑,您可以创建一个派生自 FbMigrationSqlGenerator 的 class 并只覆盖您想要的方法。
下面我使用短 table 名称逻辑来创建外键名称:
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();
}
}
额外提示:如果您想调试代码以检查问题,您可以在生成方法中添加以下行:
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
希望对您有所帮助。