问题描述
如何在运行时为C#中的测试生成,编译和应用迁移?
我想我已经很接近要回答我的问题了。我能够生成和编译迁移,但无法应用它们。在互联网上搜索两天没有任何结果。所以,有人可以帮我解决这个问题吗?
using (var scope = testServer.Host.Services.CreateScope())
{
var dbContext = (MyDbContext)scope.ServiceProvider.GetService(typeof(MyDbContext));
var migrationsDirName = "Migrations";
var migrationsAssemblyName = "My.Migrations";
var sep = Path.DirectorySeparatorChar;
var migrationsAssemblyPath = $"{migrationsDirName}{sep}{migrationsAssemblyName}.dll";
//prepare a directory
Directory.CreateDirectory(migrationsDirName);
Directory.GetFiles(migrationsDirName).ToList().ForEach(File.Delete);
//generate the migration files
var designTimeServices = new ServiceCollection()
.AddSingleton(dbContext.GetService<IHistoryRepository>())
.AddSingleton(dbContext.GetService<IMigrationsIdGenerator>())
.AddSingleton(dbContext.GetService<IMigrationsModelDiffer>())
.AddSingleton(dbContext.GetService<IMigrationsAssembly>())
.AddSingleton(dbContext.Model)
.AddSingleton(dbContext.GetService<ICurrentDbContext>())
.AddSingleton(dbContext.GetService<IDatabaseProvider>())
.AddSingleton(dbContext.GetService<IRelationalTypeMappingSource>())
.AddSingleton(dbContext.GetService<IMigrator>())
.AddSingleton<IOperationReporter>(new OperationReporter(null))
.AddSingleton<MigrationsCodeGeneratorDependencies>()
.AddSingleton<ICSharpHelper,CSharpHelper>()
.AddSingleton<CSharpMigrationoperationGeneratorDependencies>()
.AddSingleton<ICSharpMigrationoperationGenerator,CSharpMigrationoperationGenerator>()
.AddSingleton<CSharpSnapshotGeneratorDependencies>()
.AddSingleton<ICSharpSnapshotGenerator,CSharpSnapshotGenerator>()
.AddSingleton<CSharpMigrationsGeneratorDependencies>()
.AddSingleton<IMigrationsCodeGenerator,CSharpMigrationsGenerator>()
.AddSingleton<MigrationsScaffolderDependencies>()
.AddSingleton<MigrationsScaffolder>()
.AddSingleton<ISnapshotModelProcessor,SnapshotModelProcessor>()
.AddSingleton<IMigrationsCodeGeneratorSelector,MigrationsCodeGeneratorSelector>()
.BuildServiceProvider();
var scaffolder = designTimeServices.GetrequiredService<MigrationsScaffolder>();
var migration = scaffolder.ScaffoldMigration("MyMigration","My.Data");
var migrationFileName = $"{migrationsDirName}{sep}{migration.MigrationId}{migration.FileExtension}";
var designerFileNmae = $"{migrationsDirName}{sep}{migration.MigrationId}.Designer{migration.FileExtension}";
var snapshotFileName = $"{migrationsDirName}{sep}{migration.SnapshotName}{migration.FileExtension}";
File.WriteallText(migrationFileName,migration.MigrationCode);
File.WriteallText(designerFileNmae,migration.MetadataCode);
File.WriteallText(snapshotFileName,migration.SnapshotCode);
//compile the migration files
using (var migrationStream = File.OpenRead(migrationFileName))
using (var designerStream = File.OpenRead(designerFileNmae))
using (var snapshotStreamStream = File.OpenRead(snapshotFileName))
{
var compilation = CSharpCompilation
.Create(migrationsAssemblyName)
.WithOptions(new CSharpCompilationoptions(OutputKind.DynamicallyLinkedLibrary))
.AddReferences(new[]
{
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location),MetadataReference.CreateFromFile(typeof(MigrationBuilder).GetTypeInfo().Assembly.Location),MetadataReference.CreateFromFile(typeof(BaseScenario).GetTypeInfo().Assembly.Location),MetadataReference.CreateFromFile(typeof(ModelBuilder).GetTypeInfo().Assembly.Location),MetadataReference.CreateFromFile(typeof(Action).GetTypeInfo().Assembly.Location),MetadataReference.CreateFromFile(typeof(System.Runtime.AssemblyTargetedPatchBandAttribute).GetTypeInfo().Assembly.Location),MetadataReference.CreateFromFile(typeof(System.Linq.Expressions.BinaryExpression).GetTypeInfo().Assembly.Location),MetadataReference.CreateFromFile(
AppDomain.CurrentDomain.GetAssemblies().Where(a => a.GetName().Name == "netstandard").Single().Location
)
})
.AddSyntaxTrees(new[]
{
CSharpSyntaxTree.ParseText(SourceText.From(migrationStream),path: migrationFileName),CSharpSyntaxTree.ParseText(SourceText.From(designerStream),path: designerFileNmae),CSharpSyntaxTree.ParseText(SourceText.From(snapshotStreamStream),path: snapshotFileName)
});
var result = compilation.Emit(migrationsAssemblyPath);
TestMyDbContext.MigrationAssembly = migrationsAssemblyPath;
}
//apply the compiled migration to the database
dbContext = (MyDbContext)scope.ServiceProvider.GetService(typeof(MyDbContext));
dbContext.Database.EnsureDeleted();
dbContext.Database.Migrate();
}
这是我的TestMyDbContext
班:
public class TestMyDbContext : MyDbContext
{
public static string MigrationAssembly { get; set; } = null;
public TestMyDbContext(IConfiguration configuration) : base(configuration)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options
.Usesqlite("Data Source=Data/my.db",c =>
{
if(MigrationAssembly != null)
{
c.MigrationsAssembly(MigrationAssembly);
}
})
.ConfigureWarnings(warnings => warnings.Throw(RelationalEventId.QueryClientEvaluationWarning));
}
}
正如我在研究运行时迁移生成过程中发现的那样,DbContext
实例对于生成迁移是必不可少的。而且,还要在DbContext
方法中创建OnConfiguring
实例期间设置迁移程序集。
因此,我对上面的代码有以下期望:创建第一个DbContext
实例,并使用它来获取迁移程序集。之后,我创建了另一个DbContext
实例,但是这次在创建期间,我可以抓住迁移程序集,因此,新创建的DbContext
实例上的代码应应用编译后的迁移:
dbContext = (MyDbContext)scope.ServiceProvider.GetService(typeof(MyDbContext));
dbContext.Database.EnsureDeleted();
dbContext.Database.Migrate();
但是这没有发生,迁移也不会更新数据库。我在这里可能想念什么?
更新
我刚刚发现了一件有趣的事情。我下载了DB Browser for SQLite并检查了my.db
文件。它没有应用迁移。因此,我决定检查我的迁移.dll
。我只是将其复制到bin(在运行时生成后),然后注释了创建迁移程序集的代码部分(即,从var migrationsDirName = "Migrations";
行到//apply the compiled migration to the database
行),并对{ {1}}为TestMyDbContext.MigrationAssembly
名称。现在,迁移已按预期应用。但这不是我想要的,因为我不需要手动移动迁移.dll
并在每次数据模型发生更改时重新运行测试。简而言之,我能够应用迁移,但是无法以这种方式生成和编译它。
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)