我试图重写SQL迁移的默认行为发生,这样我可以指定自定义外键约束的名字,作为讨论在这里。我已按照建议连接好配置。
但是不幸的是,进展并不顺利。
快速的日志记录表明该GetFkName()
功能从未被使用过。
我尝试了一种替代配置结构,如此处和此处所讨论,但是在尝试生成迁移时出现此错误:
在程序集“ ConsoleApp1”中找到了多个迁移配置类型。指定要使用的名称。
我发现这个结果有些奇怪,因为我只有一个配置类,一个SQL生成类和一个上下文类(下面的代码没有反映这一点,但是我为实际测试注释了其他内容)。指定命令行上的配置类型,如所示在这里,在该错误:
System.InvalidOperationException:类型'ConsoleApp1.Db.CustomDbConfiguration2'不继承自'System.Data.Entity.DbConfiguration'。基于实体框架代码的配置类必须继承自“ System.Data.Entity.DbConfiguration”。
所有这些使我们回到了这里,但是由于上述原因而无法正常工作(GetFkName()
永远不会受到打击)。所以看来我在追尾巴(直到今天我才知道有一条)。
我应该怎么做才能使该替代正常工作?
组态
Imports System.Data.Entity
Imports System.Data.Entity.Migrations
Imports System.Data.Entity.SqlServer
Namespace Db
Friend Class CustomDbConfiguration
Inherits DbConfiguration
Public Sub New()
Me.SetMigrationSqlGenerator(SqlProviderServices.ProviderInvariantName, Function() New CustomSqlGenerator)
End Sub
End Class
Friend Class CustomDbConfiguration2
Inherits DbMigrationsConfiguration(Of Context)
Public Sub New()
Me.SetSqlGenerator(SqlProviderServices.ProviderInvariantName, New CustomSqlGenerator2(Me.GetSqlGenerator(SqlProviderServices.ProviderInvariantName)))
Me.ContextType = GetType(Context)
End Sub
End Class
End Namespace
SQL生成器
Imports System.Data.Entity.Migrations.Model
Imports System.Data.Entity.Migrations.Sql
Imports System.Data.Entity.SqlServer
Namespace Db
Friend Class CustomSqlGenerator
Inherits SqlServerMigrationSqlGenerator
Protected Overrides Sub Generate(AddForeignKeyOperation As AddForeignKeyOperation)
AddForeignKeyOperation.Name = GetFkName(AddForeignKeyOperation.PrincipalTable, AddForeignKeyOperation.DependentTable, AddForeignKeyOperation.DependentColumns.ToArray())
MyBase.Generate(AddForeignKeyOperation)
End Sub
Protected Overrides Sub Generate(DropForeignKeyOperation As DropForeignKeyOperation)
DropForeignKeyOperation.Name = GetFkName(DropForeignKeyOperation.PrincipalTable, DropForeignKeyOperation.DependentTable, DropForeignKeyOperation.DependentColumns.ToArray())
MyBase.Generate(DropForeignKeyOperation)
End Sub
Private Shared Function GetFkName(PrimaryKeyTable As String, ForeignKeyTable As String, ParamArray ForeignTableFields As String()) As String
IO.File.WriteAllText("D:\Logs\FkNameTest.log", $"{Now.ToString}{vbCrLf}")
Return $"FK_{ForeignKeyTable}_{PrimaryKeyTable}"
End Function
End Class
Friend Class CustomSqlGenerator2
Inherits MigrationSqlGenerator
Public Sub New(Generator As MigrationSqlGenerator)
Me.Generator = Generator
End Sub
Public Overrides Function Generate(MigrationOperations As IEnumerable(Of MigrationOperation), ProviderManifestToken As String) As IEnumerable(Of MigrationStatement)
Return Me.Generator.Generate(MigrationOperations, ProviderManifestToken)
End Function
Private ReadOnly Generator As MigrationSqlGenerator
End Class
End Namespace
语境
Imports System.Data.Common
Imports System.Data.Entity
Imports System.Data.SqlClient
Imports System.Reflection
Namespace Db
<DbConfigurationType(GetType(CustomDbConfiguration2))>
Friend Class Context
Inherits DbContext
Public Sub New()
MyBase.New(DbConnection.ConnectionString)
End Sub
Private Sub New(Connection As DbConnection)
MyBase.New(Connection, True)
Database.SetInitializer(New CreateDatabaseIfNotExists(Of Context))
Database.SetInitializer(New MigrateDatabaseToLatestVersion(Of Context, Migrations.Configuration))
Me.Database.Initialize(False)
End Sub
Public Shared Function Create() As Context
Return New Context(DbConnection)
End Function
Private Shared ReadOnly Property DbConnection As SqlConnection
Get
Return New SqlConnection(Utils.DbConnectionString)
End Get
End Property
Protected Overrides Sub OnModelCreating(Builder As DbModelBuilder)
Builder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly)
MyBase.OnModelCreating(Builder)
End Sub
Public Property Documents As DbSet(Of Document)
Public Property Sections As DbSet(Of Section)
End Class
End Namespace
免责声明:我已经很多年没有用VB编写代码了,这些代码示例是我将C#中的工作示例转换为OPs本机VB时的无用尝试。请随时更新我的语法;)
您可以通过在create table语句的一部分中调用Tablebuilder.ForeignKey时为可选参数指定值,来手动编辑迁移脚本以为每个脚本指定一个自定义名称:ForeignKey
Name
CreateTable(
"dbo.CorporationVariety",
Function(c) New With
{
.Id = c.Int(nullable: false, identity:= true),
.CorporationId = c.Int(nullable:= false),
.VarietyId = c.Int(nullable:= false),
}) _
.PrimaryKey(Function(t) t.Id)
.ForeignKey("dbo.Corporation", Function(t) t.CorporationId, name := "FKCorporatationCorporationVarietyCorporationId")
.ForeignKey("dbo.Variety", Function(t) t.VarietyId, name := "FKVarietyCorporationVarietyVarietyId")
.Index(Function(t) t.CorporationId)
.Index(Function(t) t.VarietyId)
或作为DbMigration.AddForeignKey语句的一部分:
AddForeignKey("dbo.CorporationVariety", "CorporationId", "dbo.Corporation", name := "FKCorporatationCorporationVarietyCorporationId")
AddForeignKey("dbo.CorporationVariety", "VarietyId", "dbo.Variety", name := "FKVarietyCorporationVarietyVarietyId")
如果您的模型中有很多键,并且您想在所有键上实施特定的约定(如要在给定场景中应用的标准规则或代码序列),那么通常是第一个要查找的位置解决方案是EF Code First Conventions。
不幸的是,既没有标准的约定可以帮助您,也不能使用流利的符号为外键定义自定义名称...
通常,我们会继续创建“自定义代码优先约定”来定义您的自定义逻辑,这通常以两种方式起作用:
- 您的约定通过Fluent表示法执行标准配置
- 我们已经注意到该选项对我们不可用...
- 您的约定逻辑通过注释将自定义元数据存储到模型中
在EF Code First Runtime中,主键和外键似乎是异常的,
associations
即使它们相对容易定义,似乎也没有一种方法可以轻松地从中访问注释。我很惊讶地发现了这一点,偶然发现了进一步证实这一点的帖子:https : //stackoverflow.com/a/54369685/1690217
更新我在开始本文时假设公约是正确的方法,因为多年来我将其用于许多其他需要应用的定制。如果要实现其他类似类型的自定义,请首先查看“约定”。
我们仍然可以轻松地覆盖VisualBasicMigrationCodeGenerator
生成迁移代码文件的标准,因此让我们直接跳入其中。共同应用到您的自定义名称ForeignKey
,然后实现自定义的MigrationCodeGenerator来处理约定的输出。
VisualBasicMigrationCodeGenerator
注意:这不会强制重命名数据库中的现有密钥。为此,您需要强制删除每个密钥并重新添加回去。对于大型模型,考虑到上述步骤,请考虑使用T4模板创建自定义的一次性迁移逻辑以实现此目的。
将您的Custom
VisualBasicMigrationCodeGenerator
视为您的个人EF代码第一个发酵面团文化,您可以共享此知识并将其用于每个新应用程序,并在每次迭代中添加新功能和改进。但是约定是您可能不需要在每个项目中使用的配置选项(这就是为什么将_Conventions_用于OP解决方案是我的第一个方向。)
创建一个继承自EF VisualBasicMigrationCodeGenerator的新类,我们要做的最小工作就是重写AddForeignKeyOperation
和修改键的Name并调用基本实现。这将影响添加到模型中的所有新键。
为了定位作为CreateTable的一部分添加的键,我们将必须重写GenerateInline(AddForeignKeyOperation...)
,但是基本实现(在C#Generator中)不遵循自定义名称,因此我们必须完全替换实现。
请原谅这个C#,我没有时间翻译它,尽管它确实会生成正确的VB代码;)
public class CustomVBMigrationCodeGenerator : System.Data.Entity.Migrations.Design.VisualBasicMigrationCodeGenerator
{
protected override void Generate(AddForeignKeyOperation addForeignKeyOperation, IndentedTextWriter writer)
{
ApplyCustomFKName(addForeignKeyOperation);
base.Generate(addForeignKeyOperation, writer);
}
private void ApplyCustomFKName(ForeignKeyOperation operation)
{
// expecting FK without scheme or underscores: "FK{DependentTable}{PrincipalTable}{FKField}"
operation.Name = $"FK{StripSchemeFromName(operation.DependentTable)}{StripSchemeFromName(operation.PrincipalTable)}{String.Join("", operation.DependentColumns)}";
}
private string StripSchemeFromName(string dbObjectName)
{
return dbObjectName.Split(new[] { '.' }, 2).Last();
}
/// <summary>
/// Generates code to perform an <see cref="AddForeignKeyOperation" /> as part of a <see cref="CreateTableOperation" />.
/// </summary>
/// <param name="addForeignKeyOperation"> The operation to generate code for. </param>
/// <param name="writer"> Text writer to add the generated code to. </param>
protected virtual void GenerateInline(AddForeignKeyOperation addForeignKeyOperation, IndentedTextWriter writer)
{
// sourced from https://github.com/aspnet/EntityFramework6/blob/master/src/EntityFramework/Migrations/Design/VisualBasicMigrationCodeGenerator.cs
Check.NotNull(addForeignKeyOperation, "addForeignKeyOperation");
Check.NotNull(writer, "writer");
writer.WriteLine(" _");
writer.Write(".ForeignKey(" + Quote(addForeignKeyOperation.PrincipalTable) + ", ");
Generate(addForeignKeyOperation.DependentColumns, writer);
// Our Custom logic
ApplyCustomFKName(addForeignKeyOperation);
// Insert our custom name if provided
if (!addForeignKeyOperation.HasDefaultName)
{
writer.Write(", name := " + Quote(addForeignKeyOperation.Name));
}
if (addForeignKeyOperation.CascadeDelete)
{
writer.Write(", cascadeDelete := True");
}
writer.Write(")");
}
}
在您的项目中找到Configuration.vb,将构造方法设置CodeGenerator
为您的实例CustomVBMigrationCodeGenerator
:
Public Sub New()
AutomaticMigrationsEnabled = false
CodeGenerator = new CustomVBMigrationCodeGenerator()
End Sub
现在执行add-migration
来生成新的迁移,您将看到在迁移脚本中定义的新的自定义名称。
Generate(DropForeignKeyOperation...)
如果您需要从此配置_or_降级,并且如果alter table命令要求先删除密钥,则可能需要同样重写这些方法。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句