繁体   English   中英

EF Core 2.1中的动态数据种子迁移生成取决于DbContext bool-flag以及OnModelCreating()中的用法

[英]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()获取要播种的对象数组。 该数组将从一个单独的类( TemplateReader )提供,该类从JSON文件加载对象(其中将完成扩展和修改工作)。 因此,可以修改JSON文件的内容,并添加包含生成的代码的新迁移到Insert( modelBuilder.InsertData() ),Update( modelBuilder.UpdateData() )或Delete( 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);
    }
}

TemplateTypeConfiguration

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的值被赋予TemplateTypeConfiguration时,它的值为false
因此,如果我修改了上述JSON文件,则Add-Migration导致“空”迁移而没有任何逻辑。


已经过测试的方法

我已经尝试将IModelCacheKeyFactory与自己的ModelCacheKey对象一起使用,但没有成功。 作为模板,我使用了这个SO问题

我测试的另一种方法是将_shouldSeedData设置为public static变量并将其从MyDesignTimeDbContextFactory设置为true ,但在我看来,这是一个非常脏的解决方案,我想避免在生产代码中实现。

也应该可以使用DbContextOptionsBuilder<T>UseModel(IModel)扩展方法来避免使用OnModelCreating并使用所需的shouldSeedData = false初始化TemplateTypeConfiguration 这种方法的缺点是具有复制的代码,这些代码在TemplateTypeConfiguration的构造函数值方面会有所不同。 在我看来,作为公共静态方法令人讨厌。


是否有一个干净的解决方案来实现通过构造函数设置_shouldSeedDataOnModelCreating可以在设计时使用正确的值( true )?
在生产中它应该是false并且由于if-condition,不应该调用TemplateReader中的TemplateTypeConfiguration

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

在您的具体方案中, OnModelCreating由存储传递的参数之前访问ChangeTracker属性触发:

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

    _shouldSeedData = shouldSeedData;
}

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM