gpt4 book ai didi

c# - 如何使用 Entity Framework Core 功能来更改我的数据库?

转载 作者:行者123 更新时间:2023-11-30 16:41:03 25 4
gpt4 key购买 nike

我正在 Entity Framework Core 之上构建一个应用程序,我想在某种程度上在运行时应用迁移。

我打算采用的方法是将当前数据库模型保存在内存中并创建一个新模型,然后使用 IMigrationsModelDiffer.GetDifferences() 计算两个模型之间的差异。

从那里开始,我不想将差异打印到 Migration 类中,而是想直接创建 MigrationCommand 并将这些命令应用到我的数据库。

上面的内容听起来相当简单,但我在依赖注入(inject)系统方面遇到了很多问题。

这是我现在的代码:

static DbContextOptions GetOptions(IModel model)
{
var builder = new DbContextOptionsBuilder();
builder
.UseSqlServer(connStr)
.UseModel(model);
return builder.Options;
}
class Test1ModelAEntity
{
public int Id { get; set; }
public string StrProp { get; set; }
}
static void Main(string[] args)
{
var sqlServerServices = new ServiceCollection()
.AddEntityFrameworkSqlServer()
.BuildServiceProvider();
var conventions = new ConventionSet();
sqlServerServices.GetRequiredService<IConventionSetBuilder>().AddConventions(conventions);

var emptyModelBuilder = new ModelBuilder(conventions);
var emptyModel = emptyModelBuilder.Model;

var test1ModelBuilder = new ModelBuilder(conventions);
test1ModelBuilder.Entity<Test1ModelAEntity>()
.ToTable("ModelA");
var test1Model = test1ModelBuilder.Model;

using (TestContext ctx = new TestContext(GetOptions(emptyModel)))
{
var migrationServices = new ServiceCollection()
.AddDbContextDesignTimeServices(ctx)
.AddEntityFrameworkSqlServer()
.BuildServiceProvider();
var operations = migrationServices.GetRequiredService<IMigrationsModelDiffer>().GetDifferences(emptyModel, test1Model);
var commands = migrationServices.GetRequiredService<IMigrationsSqlGenerator>().Generate(operations, test1Model);
var connection = migrationServices.GetRequiredService<IRelationalConnection>();
migrationServices.GetRequiredService<IMigrationCommandExecutor>().ExecuteNonQuery(commands, connection);
}
}

这段代码抛出一个 NullReferenceException 和这个堆栈跟踪:

at Microsoft.EntityFrameworkCore.Metadata.Internal.TableMapping.<>c.<GetRootType>b__10_0(IEntityType t)
at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsModelDiffer.GetSortedProperties(TableMapping target)
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsModelDiffer.<Add>d__37.MoveNext()
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsModelDiffer.<DiffCollection>d__73`1.MoveNext()
at System.Linq.Enumerable.ConcatIterator`1.MoveNext()
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationsModelDiffer.Sort(IEnumerable`1 operations, DiffContext diffContext)
at Sandbox.Program.Main(String[] args) in D:\Sandbox\Program.cs:line 108

我检查了源代码,似乎 EFCore 解释我的模型的方式存在问题。我正在使用 EFCore 版本 2.1 预览版 2。

实际上,我主要是在我的 IServiceCollection 上尝试随机配置,因为我不知道如何设置它。我也试图远离 EFCore 内部类,但如果需要,我可能暂时使用一两个。

有没有办法利用 EFCore 的内置功能在给定一对 IModel 的情况下生成一些 SQL?如果是这样,我该如何设置 DI 以获得所有必需的服务?

最佳答案

感谢您给我指出正确方向的评论。

总而言之,我尝试使用一个空的约定集来创建我的模型。这显然会导致各种问题,因为您必须显式生成整个模型,这非常复杂。

要使用预期的约定集,我必须使用 ConventionSet.CreateConventionSet 从上下文中获取它。在能够在查询和插入命令中使用它之前,我还必须手动验证我的模型。其余逻辑几乎相同。

这是我的最终代码,包括我为确保一切按预期运行而运行的测试:

static DbContextOptions GetOptions(IModel model)
{
var builder = new DbContextOptionsBuilder();
builder
.UseSqlServer(connStr)
.UseModel(model);
return builder.Options;
}

//Test 1
class Test1EntityA
{
public int Id { get; set; }
public string StrProp { get; set; }
}

//Test 2
class Test2EntityA
{
public int Id { get; set; }
public string StrProp { get; set; }
public ICollection<Test2ModelBEntity> Children { get; set; }
}
class Test2EntityB
{
public int Id { get; set; }
public int EntityAId { get; set; }
public Test2EntityA EntityA { get; set; }
}

static void Main(string[] args)
{
var emptyModelBuilder = new ModelBuilder(new ConventionSet());
var emptyModel = emptyModelBuilder.Model;
using (var baseCtx = new TestContext(GetOptions(emptyModel)))
{
//Get all services we need from the base context
var conventions = ConventionSet.CreateConventionSet(baseCtx);
var migrationServices = new ServiceCollection()
.AddDbContextDesignTimeServices(baseCtx)
.AddEntityFrameworkSqlServer()
.BuildServiceProvider();

//Test 1
var test1ModelBuilder = new ModelBuilder(conventions);
test1ModelBuilder.Entity<Test1EntityA>()
.ToTable("EntityA");
var test1Model = test1ModelBuilder.GetInfrastructure().Metadata;
test1Model.Validate();

var operations = migrationServices.GetRequiredService<IMigrationsModelDiffer>().GetDifferences(emptyModel, test1Model);
var commands = migrationServices.GetRequiredService<IMigrationsSqlGenerator>().Generate(operations, test1Model);
var connection = migrationServices.GetRequiredService<IRelationalConnection>();
migrationServices.GetRequiredService<IMigrationCommandExecutor>().ExecuteNonQuery(commands, connection);

using (TestContext ctx = new TestContext(GetOptions(test1Model)))
{
ctx.Set<Test1EntityA>().Add(new Test1EntityA
{
StrProp = "test1",
});
ctx.SaveChanges();
}

//Test 2
var test2ModelBuilder = new ModelBuilder(conventions);
test2ModelBuilder.Entity<Test2EntityA>()
.ToTable("EntityA");
test2ModelBuilder.Entity<Test2EntityB>()
.ToTable("EntityB");
var test2Model = test2ModelBuilder.GetInfrastructure().Metadata;
test2Model.Validate();

operations = migrationServices.GetRequiredService<IMigrationsModelDiffer>().GetDifferences(test1Model, test2Model);
commands = migrationServices.GetRequiredService<IMigrationsSqlGenerator>().Generate(operations, test2Model);
migrationServices.GetRequiredService<IMigrationCommandExecutor>().ExecuteNonQuery(commands, connection);

using (TestContext ctx = new TestContext(GetOptions(test2Model)))
{
var e = new Test2EntityA
{
StrProp = "test2",
};
ctx.Set<Test2EntityA>().Add(e);
ctx.Set<Test2EntityB>().Add(new Test2EntityB
{
EntityA = e,
});
ctx.SaveChanges();

Console.WriteLine(ctx.Set<Test2EntityB>().Where(b => b.EntityA.StrProp == "test2").Count());
}
}
}

关于c# - 如何使用 Entity Framework Core 功能来更改我的数据库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49946019/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com