我的DbContext
中有一个特殊的基表类型。从它继承后,我需要生成一个附加的“ SQL”迁移操作来为其创建特定的触发器。通过检查重叠范围,确保表结构是一致的。由于SQL Server中没有重叠的索引或检查约束,因此我不得不使用触发器(在检查约束中使用函数会导致迁移以及SQL中混乱的函数“命名空间”带来同样的问题。)
由于在OnModelCreating
期间找不到任何创建触发器的方法,所以我想到了更改生成的迁移。但是该怎么做?
尝试使用SqlServerMigrationsSqlGenerator
和SqlServerMigrationsAnnotationProvider
,但顾名思义,它们仅在SQL命令生成期间的最后阶段使用。使用迁移时,这使它们有点看不见。难以在需要时进行自定义,然后再进行维护。
考虑使用CSharpMigrationOperationGenerator
似乎很适合我的需求。但是有一个问题-我无法访问此类。也不是名称空间。
根据来源,此类位于Microsoft.EntityFrameworkCore.Migrations.Design
命名空间中,并且是公共的。为了访问它,必须安装Microsoft.EntityFrameworkCore.Design
软件包。
但这不起作用。
我在这里想念什么?如何访问和继承此类?也许有更好更好的方法可以在特定表的迁移过程中自动创建触发器?
答案 0 :(得分:2)
ICSharpMigrationOperationGenerator
实现考虑使用CSharpMigrationOperationGenerator,这似乎很适合我的需求。但是有一个问题-我无法访问此类。也不是名称空间。
根据源此类,此类位于Microsoft.EntityFrameworkCore.Migrations.Design命名空间中,并且是公共的。为了访问它,必须安装Microsoft.EntityFrameworkCore.Design程序包。
但这不起作用。
我在这里想念什么? 如何访问和继承此类?
假设您正在调用以下CLI命令在设计时添加新的迁移:
dotnet ef migrations add "SomeMigration"
这是一个可以正常运行的示例控制台程序,它将使用自ICSharpMigrationOperationGenerator
继承的名为MyCSharpMigrationOperationGenerator
的自定义CSharpMigrationOperationGenerator
实现:
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Migrations.Design;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
public class MyCSharpMigrationOperationGenerator : CSharpMigrationOperationGenerator
{
public MyCSharpMigrationOperationGenerator(CSharpMigrationOperationGeneratorDependencies dependencies)
: base(dependencies)
{
}
protected override void Generate(CreateTableOperation operation, IndentedStringBuilder builder)
{
Console.WriteLine("\r\n\r\n---\r\nMyCSharpMigrationOperationGenerator was used\r\n---\r\n");
base.Generate(operation, builder);
}
}
public class MyDesignTimeServices : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection services)
=> services.AddSingleton<ICSharpMigrationOperationGenerator, MyCSharpMigrationOperationGenerator>();
}
public class IceCream
{
public int IceCreamId { get; set; }
public string Name { get; set; }
}
public class Context : DbContext
{
public DbSet<IceCream> IceCreams { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseMySQL(@"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So63575132")
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
}
internal static class Program
{
private static void Main()
{
}
}
}
MyCSharpMigrationOperationGenerator
类为每个添加的表输出以下行,以证明其被调用:
---
MyCSharpMigrationOperationGenerator was used
---
正如@KasbolatKumakhov在其评论中指出的那样,还应牢记从2.2引用Microsoft.EntityFrameworkCore.Design
has been changed的方式。到3.0:
从EF Core 3.0开始,它是一个DevelopmentDependency程序包。这意味着该依赖项不会传递到其他项目中,并且默认情况下您不能再引用其程序集。 [...] 如果需要引用此程序包以覆盖EF Core的设计时行为,则可以在项目中更新PackageReference项元数据。
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.0.0"> <PrivateAssets>all</PrivateAssets> <!-- Remove IncludeAssets to allow compiling against the assembly --> <!--<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>--> </PackageReference>
MigrationOperation
(例如,用于创建触发器)由于在OnModelCreating期间找不到任何创建触发器的方法,所以我想到了更改生成的迁移。但是该怎么做?
要正确执行此操作,您需要执行以下操作:
MyPrefix:Trigger
)MigrationOperation
(例如CreateTriggerMigrationOperation
)IMigrationsModelDiffer
实现(源自MigrationsModelDiffer
;这是内部的),该实现返回您自己的MigrationOperation
ICSharpMigrationOperationGenerator
实现(源自CSharpMigrationOperationGenerator
),然后为您自己的MigrationOperation
生成C#代码IMigrationsSqlGenerator
实现(源自SqlServerMigrationsSqlGenerator
),然后执行将您自己的MigrationOperation
转换为SQL 答案 1 :(得分:1)
这不完全是您所要求的,但它以低成本完成了类似的工作,并且可能对某人有用。
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
public static class MigrationBuilderExtensions
{
public static void ConfigForOracle(this MigrationBuilder migrationBuilder)
{
//For each table registered in the builder, let's create a sequence and a trigger
foreach (CreateTableOperation createTableOperation in migrationBuilder.Operations.ToArray().OfType<CreateTableOperation>())
{
string tableName = createTableOperation.Name;
string primaryKey = createTableOperation.PrimaryKey.Columns[0];
migrationBuilder.CreateSequence<int>(name: $"SQ_{tableName}", schema: createTableOperation.Schema);
migrationBuilder.Sql($@"CREATE OR REPLACE TRIGGER ""TR_{tableName}""
BEFORE INSERT ON ""{tableName}""
FOR EACH ROW
WHEN (new.""{primaryKey}"" IS NULL)
BEGIN
SELECT ""SQ_{tableName}"".NEXTVAL
INTO :new.""{primaryKey}""
FROM dual;
END;");
}
}
}
您可以在扩展方法中做任何您想做的事情,然后在 Migration.Up()
方法的末尾调用它。我使用它为 Oracle 11g 表创建序列和触发器以进行标识符增量。
答案 2 :(得分:0)
打开您的迁移文件并更改您的Up
方法。
然后使用软件包管理器控制台中的Update-Database
应用迁移。
类似这样的东西:
public partial class CreateDatabase : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql("Some custom SQL statement");
migrationBuilder.CreateTable(
name: "Authors",
columns: table => new
{
AuthorId = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
FirstName = table.Column<string>(nullable: true),
LastName = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Authors", x => x.AuthorId);
});
}
}
答案 3 :(得分:0)
我认为不打算修改ef核心csharp代码生成。 但是要生成自定义迁移语句(在我的情况下为触发器),我会使用SqlOperation进行以下操作(简称为相关操作)。
实施ModelDiffer
public class MyMigrationsModelDiffer : MigrationsModelDiffer {
public MyMigrationsModelDiffer(IRelationalTypeMappingSource typeMappingSource,
IMigrationsAnnotationProvider migrationsAnnotations,
IChangeDetector changeDetector,
IUpdateAdapterFactory updateAdapterFactory,
CommandBatchPreparerDependencies commandBatchPreparerDependencies)
: base(typeMappingSource, migrationsAnnotations, changeDetector, updateAdapterFactory, commandBatchPreparerDependencies) { }
protected override IEnumerable<MigrationOperation> Diff(IModel source, IModel target, DiffContext diffContext) {
return base.Diff(source, target, diffContext).Concat(GetTriggerTriggerDifferences(source, target));
}
public override Boolean HasDifferences(IModel source, IModel target) {
return base.HasDifferences(source, target) || HasTriggerAnnotationDifferences(source, target);
}
public IEnumerable<MigrationOperation> GetTriggerTriggerDifferences(IModel source, IModel target) {
if (source == null || target == null) {
return new new List<MigrationOperation>(0);
}
Dictionary<String, IAnnotation> triggerAnnotationPerEntity = new Dictionary<String, IAnnotation>();
foreach (var entityType in source.GetEntityTypes()) {
triggerAnnotationPerEntity[entityType.Name] = GetTableAnnotation(entityType);
}
var operations = new List<MigrationOperation>();
foreach (var entityType in target.GetEntityTypes()) {
triggerAnnotationPerEntity.TryGetValue(entityType.Name, out IAnnotation sourceTriggerTable);
IAnnotation targetTriggerTable = GetTableAnnotation(entityType);
if (targetTriggerTable?.Value == sourceTriggerTable?.Value) {
continue;
}
Boolean isCreate = targetTriggerTable != null;
String tableName = (entityType as EntityType)?.GetTableName();
String primaryKey = entityType.FindPrimaryKey().Properties[0].Name;
if (isCreate) {
SqlOperation sqlOperation = new SqlOperation();
sqlOperation.Sql = $@"CREATE TRIGGER...";
operations.Add(sqlOperation);
}
else {
// drop trigger sqloperation
}
}
return operations;
}
private static IAnnotation GetTableAnnotation(IEntityType entityType) {
return entityType.GetAnnotations()?.FirstOrDefault(x => x.Name == "WantTrigger");
}
public Boolean HasTriggerAnnotationDifferences(IModel source, IModel target) {
return GetTriggerTriggerDifferences(source, target).Any();
}
}
替换您的DbContext中的模型
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
base.OnConfiguring(optionsBuilder);
if (optionsBuilder == null) {
return;
}
optionsBuilder.ReplaceService<IMigrationsModelDiffer, MyMigrationsModelDiffer>();
}
用注释标记所需的模型。
builder.Entity<MyTable>().HasAnnotation("WantTrigger", "1.0");