EF Core 2.1 中的动态数据播种迁移生成取决于在 OnModelCreating() 中使用的 DbContext bool-flag
Dynamic data seeding migration-generation in EF Core 2.1 depending on DbContext bool-flag with usage in OnModelCreating()
现状
你好,我有一个 dotnet 标准库,我在其中使用 EF Core 2.1.1(代码优先方法)来访问持久层。为了创建迁移,我使用了一个单独的 dotnet 核心控制台应用程序(在同一解决方案中),其中包含一个 IDesignTimeDbContextFactory<T>
实现。
有必要播种一些数据,我想以一种舒适的方式实现它,因为将来要播种的数据将被扩展或修改。因此,在实现的 IEntityTypeConfiguration
中,我使用扩展方法 .HasData()
获取要播种的对象数组。该数组将从一个单独的 class (TemplateReader
) 提供,它从 JSON 文件加载对象(将在其中完成扩展和修改工作)。因此,可以修改 JSON 文件的内容并添加一个包含生成代码的新迁移以插入 (modelBuilder.InsertData()
)、更新 (modelBuilder.UpdateData()
) 或删除 (modelBuilder.DeleteData()
) 语句。
因为我不会发布 JSON 文件,并且我想避免加载序列化数据以进行播种和执行 .HasData()
,我想使用一个 bool
值,该值将被赋予 DbContext
由构造函数。
为了避免在不需要为迁移调用种子设定(和 .HasData()
)时使用 bool 值,我有一个重载的构造函数,它使用默认值 false 实现。
此外,我不会使用 OnConfiguring
因为我想灵活地在我的 IoC 容器中或单独设置 DbContextOptions<T>
对象进行测试。
代码
以下代码包含重命名的变量以更加匿名地描述项目的内容,但代表当前实现的逻辑。
MyDesignTimeDbContextFactory:
public class MyDesignTimeDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
{
public MyDbContext CreateDbContext(string[] args)
{
var connectionString = ConfigurationManager.ConnectionStrings["SqlServer"].ConnectionString;
var contextOptionsBuilder = new DbContextOptionsBuilder<MyDbContext>()
.UseSqlServer(connectionString);
return new MyDbContext(contextOptionsBuilder.Options, true);
}
}
MyDbContext:
public sealed class MyDbContext : DbContext
{
private readonly bool _shouldSeedData;
public DbSet<Content> Contents { get; set; }
public DbSet<Template> Templates { get; set; }
public MyDbContext(DbContextOptions<MyDbContext> options, bool shouldSeedData = false) :
base(options)
{
ChangeTracker.LazyLoadingEnabled = false;
_shouldSeedData = shouldSeedData;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("mySchema");
modelBuilder.ApplyConfiguration(new TemplateTypeConfiguration(_shouldSeedData));
base.OnModelCreating(modelBuilder);
}
}
模板类型配置:
public class TemplateTypeConfiguration : IEntityTypeConfiguration<Template>
{
private readonly bool _shouldSeedData;
public TemplateTypeConfiguration(bool shouldSeedData)
{
_shouldSeedData = shouldSeedData;
}
public void Configure(EntityTypeBuilder<Template> builder)
{
builder.Property(p => p.ModuleKey)
.IsRequired();
builder.Property(p => p.Html)
.IsRequired();
if (_shouldSeedData)
{
// reads all templates from configuration file for seeding
var templateReader = new TemplateReader(Directory.GetCurrentDirectory());
var templates = templateReader.GetTemplates().ToArray();
builder.HasData(templates);
}
}
}
模板(实体):
public class Template
{
public int Id { get; set; }
public string ModuleKey { get; set; }
public string Html { get; set; }
public virtual ICollection<Content> Contents { get; set; }
}
问题
据我所知并且我已经测试过 OnModelCreating(ModelBuilder)
将在构造函数中设置构造函数的 bool 值之前被调用 (_shouldSeedData = shouldSeedData;
)。那是因为 base constructor 将被立即调用,然后是我的。因此 _shouldSeedData
的值是 false
当它将被赋予 TemplateTypeConfiguration
.
因此,如果我修改了上述 JSON 文件,Add-Migration
会导致没有任何逻辑的 "empty" 迁移。
已经测试过的方法
我已经尝试将 IModelCacheKeyFactory
与自己的 ModelCacheKey
对象一起使用,但没有成功。作为模板,我使用了这个 .
我测试的另一种方法是将 _shouldSeedData
设置为 public static
变量并将其从 MyDesignTimeDbContextFactory
设置为 true
,但在我看来,这是一个非常肮脏的方法我想避免在生产代码中实施的解决方案。
也应该可以使用 DbContextOptionsBuilder<T>
的 UseModel(IModel)
扩展方法来避免使用 OnModelCreating
并用需要的 shouldSeedData = false
初始化 TemplateTypeConfiguration
.这种方法的一个缺点是有重复的代码,这些代码将在 TemplateTypeConfiguration
的构造函数值上有所不同。在我看来,与 public 静态方法一样令人讨厌。
问题
是否有一个干净的解决方案来实现由构造函数设置 _shouldSeedData
,使 OnModelCreating
可以在设计时使用正确的值 (true
)?
在生产中它应该是 false
并且在 TemplateTypeConfiguration
中提到的 TemplateReader
不应该被调用,因为 if-condition.
OnModelCreating
是不是由基础DbContext
构造函数调用触发的,所以将传递的参数保存到[=23=是绝对没有问题的] 成员。
在您的具体场景中,OnModelCreating
是通过在存储传递的参数之前访问 ChangeTracker
属性 触发的:
public MyDbContext(DbContextOptions<MyDbContext> options, bool shouldSeedData = false) :
base(options)
{
ChangeTracker.LazyLoadingEnabled = false; // <--
_shouldSeedData = shouldSeedData;
}
简单的调换线路,问题就迎刃而解了。通常,在访问任何上下文 属性.
之前始终初始化您的 class 成员
现状
你好,我有一个 dotnet 标准库,我在其中使用 EF Core 2.1.1(代码优先方法)来访问持久层。为了创建迁移,我使用了一个单独的 dotnet 核心控制台应用程序(在同一解决方案中),其中包含一个 IDesignTimeDbContextFactory<T>
实现。
有必要播种一些数据,我想以一种舒适的方式实现它,因为将来要播种的数据将被扩展或修改。因此,在实现的 IEntityTypeConfiguration
中,我使用扩展方法 .HasData()
获取要播种的对象数组。该数组将从一个单独的 class (TemplateReader
) 提供,它从 JSON 文件加载对象(将在其中完成扩展和修改工作)。因此,可以修改 JSON 文件的内容并添加一个包含生成代码的新迁移以插入 (modelBuilder.InsertData()
)、更新 (modelBuilder.UpdateData()
) 或删除 (modelBuilder.DeleteData()
) 语句。
因为我不会发布 JSON 文件,并且我想避免加载序列化数据以进行播种和执行 .HasData()
,我想使用一个 bool
值,该值将被赋予 DbContext
由构造函数。
为了避免在不需要为迁移调用种子设定(和 .HasData()
)时使用 bool 值,我有一个重载的构造函数,它使用默认值 false 实现。
此外,我不会使用 OnConfiguring
因为我想灵活地在我的 IoC 容器中或单独设置 DbContextOptions<T>
对象进行测试。
代码
以下代码包含重命名的变量以更加匿名地描述项目的内容,但代表当前实现的逻辑。
MyDesignTimeDbContextFactory:
public class MyDesignTimeDbContextFactory : IDesignTimeDbContextFactory<MyDbContext>
{
public MyDbContext CreateDbContext(string[] args)
{
var connectionString = ConfigurationManager.ConnectionStrings["SqlServer"].ConnectionString;
var contextOptionsBuilder = new DbContextOptionsBuilder<MyDbContext>()
.UseSqlServer(connectionString);
return new MyDbContext(contextOptionsBuilder.Options, true);
}
}
MyDbContext:
public sealed class MyDbContext : DbContext
{
private readonly bool _shouldSeedData;
public DbSet<Content> Contents { get; set; }
public DbSet<Template> Templates { get; set; }
public MyDbContext(DbContextOptions<MyDbContext> options, bool shouldSeedData = false) :
base(options)
{
ChangeTracker.LazyLoadingEnabled = false;
_shouldSeedData = shouldSeedData;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultSchema("mySchema");
modelBuilder.ApplyConfiguration(new TemplateTypeConfiguration(_shouldSeedData));
base.OnModelCreating(modelBuilder);
}
}
模板类型配置:
public class TemplateTypeConfiguration : IEntityTypeConfiguration<Template>
{
private readonly bool _shouldSeedData;
public TemplateTypeConfiguration(bool shouldSeedData)
{
_shouldSeedData = shouldSeedData;
}
public void Configure(EntityTypeBuilder<Template> builder)
{
builder.Property(p => p.ModuleKey)
.IsRequired();
builder.Property(p => p.Html)
.IsRequired();
if (_shouldSeedData)
{
// reads all templates from configuration file for seeding
var templateReader = new TemplateReader(Directory.GetCurrentDirectory());
var templates = templateReader.GetTemplates().ToArray();
builder.HasData(templates);
}
}
}
模板(实体):
public class Template
{
public int Id { get; set; }
public string ModuleKey { get; set; }
public string Html { get; set; }
public virtual ICollection<Content> Contents { get; set; }
}
问题
据我所知并且我已经测试过 OnModelCreating(ModelBuilder)
将在构造函数中设置构造函数的 bool 值之前被调用 (_shouldSeedData = shouldSeedData;
)。那是因为 base constructor 将被立即调用,然后是我的。因此 _shouldSeedData
的值是 false
当它将被赋予 TemplateTypeConfiguration
.
因此,如果我修改了上述 JSON 文件,Add-Migration
会导致没有任何逻辑的 "empty" 迁移。
已经测试过的方法
我已经尝试将 IModelCacheKeyFactory
与自己的 ModelCacheKey
对象一起使用,但没有成功。作为模板,我使用了这个
我测试的另一种方法是将 _shouldSeedData
设置为 public static
变量并将其从 MyDesignTimeDbContextFactory
设置为 true
,但在我看来,这是一个非常肮脏的方法我想避免在生产代码中实施的解决方案。
也应该可以使用 DbContextOptionsBuilder<T>
的 UseModel(IModel)
扩展方法来避免使用 OnModelCreating
并用需要的 shouldSeedData = false
初始化 TemplateTypeConfiguration
.这种方法的一个缺点是有重复的代码,这些代码将在 TemplateTypeConfiguration
的构造函数值上有所不同。在我看来,与 public 静态方法一样令人讨厌。
问题
是否有一个干净的解决方案来实现由构造函数设置 _shouldSeedData
,使 OnModelCreating
可以在设计时使用正确的值 (true
)?
在生产中它应该是 false
并且在 TemplateTypeConfiguration
中提到的 TemplateReader
不应该被调用,因为 if-condition.
OnModelCreating
是不是由基础DbContext
构造函数调用触发的,所以将传递的参数保存到[=23=是绝对没有问题的] 成员。
在您的具体场景中,OnModelCreating
是通过在存储传递的参数之前访问 ChangeTracker
属性 触发的:
public MyDbContext(DbContextOptions<MyDbContext> options, bool shouldSeedData = false) :
base(options)
{
ChangeTracker.LazyLoadingEnabled = false; // <--
_shouldSeedData = shouldSeedData;
}
简单的调换线路,问题就迎刃而解了。通常,在访问任何上下文 属性.
之前始终初始化您的 class 成员