代码之家  ›  专栏  ›  技术社区  ›  ChW

ef core 2.1中的动态数据种子植入迁移生成取决于onmodelcreating()中使用的dbcontext bool标志

  •  4
  • ChW  · 技术社区  · 7 年前

    现状

    你好,我有一个dotnet标准库 EF核心2.1.1 (代码优先方法)访问持久层。要创建迁移,我使用一个单独的dotnet核心控制台应用程序(在同一个解决方案中),该应用程序包含 IDesignTimeDbContextFactory<T> 实施。
    有必要播种一些数据,我想以一种舒适的方式来实现它,因为将来要播种的数据将被扩展或修改。因此在实施中 IEntityTypeConfiguration 我使用扩展方法 .HasData() 它获取要播种的对象数组。数组将从一个单独的类提供( TemplateReader ,它从json文件加载对象(在该文件中将完成扩展和修改工作)。因此,可以修改json文件的内容并添加新的迁移,该迁移将包含要插入的生成代码( modelBuilder.InsertData() ),更新( modelBuilder.UpdateData() )或删除( modelBuilder.DeleteData() )陈述。
    因为我不会发送json文件,并且我希望避免加载序列化数据以进行种子设定和执行 .hasdata() ,我想使用 bool 将被赋予的价值 DbContext 由构造器。
    如果不需要为迁移调用种子,则避免使用bool值(和 .hasdata() )我用默认值false实现了一个重载构造函数。 此外,我不会使用 OnConfiguring 因为我要灵活设置 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; )中。那是因为 基本构造函数 会马上打电话给我,然后是我的。因此 _shouldSeedData 的值为 false 什么时候给 TemplateTypeConfiguration 是的。
    因此,一个 Add-Migration 如果我修改了上面提到的json文件,将导致一个没有任何逻辑的“空”迁移。


    已经测试过的方法

    我已经试过用 IModelCacheKeyFactory 有自己的 ModelCacheKey 反对没有任何成功。作为模板,我用了这个 SO-question 是的。

    我测试的另一种方法是 _应该播种数据 作为 public static 变量并将其设置为 MyDesignTimeDbContextFactory true ,但在我看来,这是一个非常肮脏的解决方案,我希望避免在生产代码中实现。

    也可以使用 DbContextOptionsBuilder<T> UseModel(IModel) 避免使用 OnModelCreating 以及初始化 模板类型配置 有需要的 shouldSeedData = false 是的。这种方法的缺点是重复的代码在 模板类型配置 的构造函数值。在我看来,这和公众的静态做法一样令人讨厌。


    问题

    有没有一个干净的解决方案来实现 _应该播种数据 建造商认为 模型创建 可以用正确的值( 真的 )在设计时间?
    在生产过程中 并提到 模板管理员 在里面 模板类型配置 不应因为if条件而调用。

    1 回复  |  直到 7 年前
        1
  •  4
  •   Ivan Stoev    7 年前

    OnModelCreating 由基地触发 DbContext 构造函数调用,因此将传递的参数保存到类成员绝对没有问题。

    在你的具体场景中, 模型创建 由访问 ChangeTracker 属性,然后再存储传递的参数:

    public MyDbContext(DbContextOptions<MyDbContext> options, bool shouldSeedData = false) :
        base(options)
    {
        ChangeTracker.LazyLoadingEnabled = false; // <--
    
        _shouldSeedData = shouldSeedData;
    }
    

    只要换线,问题就解决了。通常,在访问任何上下文属性之前,都要初始化类成员。

    推荐文章