简体   繁体   English

创建表后 EFCore EnsureCreated() 运行命令

[英]EFCore EnsureCreated() run command after table creation

So I'm pretty new to EFCore (straight into 5.0 preview, because why not), and enjoying all the fluent configuration side of things.所以我对 EFCore 很陌生(直接进入 5.0 预览版,因为为什么不),并且享受所有流畅的配置方面的事情。 The whole IEntityTypeConfiguration<> concept really helps in making the Entities agnostic to the backing DB (as opposed to using attributes on the entities themselves).整个IEntityTypeConfiguration<>概念确实有助于使实体与后备数据库无关(而不是在实体本身上使用属性)。

I generate my whole database using DbContext.Database.EnsureCreated() in my Startup.cs .我在Startup.cs中使用DbContext.Database.EnsureCreated()生成整个数据库。 It's currently backed by SQLite while I'm playing around, but would like to eventually move to PostgreSQL with the TimescaleDB extension.它目前由 SQLite 支持,而我正在玩,但最终希望通过 TimescaleDB 扩展迁移到 PostgreSQL。

The thing is, when using TimescaleDB, I would need to issue a create_hypertable() immediately after creating the table itself.问题是,在使用 TimescaleDB 时,我需要在创建表本身后立即发出create_hypertable()

What's the best way to issue a command after table creation?创建表后发出命令的最佳方法是什么?

  • Right after EnsureCreated() , using ExecuteSqlRaw() ?EnsureCreated()之后,使用ExecuteSqlRaw()吗? That means all the tables have been created before the SQL is executed.这意味着在执行 SQL 之前已经创建了所有表。 I would prefer having it as close to the table creation as possible.我希望它尽可能接近表创建。
  • Somewhere inside the DbContext ? DbContext内部的某个地方?
  • In my IEntityTypeConfiguration<> ?在我的IEntityTypeConfiguration<>
  • or somewhere else?或者别的地方?

Thanks!谢谢!

If you want to keep using context.Database.EnsureCreated() , then running your own script (eg by executing context.Database.ExecuteSqlRaw() ) after the call is the way to go here.如果您想继续使用context.Database.EnsureCreated() ,那么在调用之后运行您自己的脚本(例如,通过执行context.Database.ExecuteSqlRaw() )是 go 的方式。

That means all the tables have been created before the SQL is executed.这意味着在执行 SQL 之前已经创建了所有表。 I would prefer having it as close to the table creation as possible.我希望它尽可能接近表创建。

There shouldn't be any downsides to this as far as I can see, so you are not gaining anything by moving the create_hypertable() call closer to another table (if you disagree, please elaborate why this is the case).据我所知,这不应该有任何缺点,因此通过将create_hypertable()调用移近另一个表,您不会获得任何好处(如果您不同意,请详细说明为什么会这样)。


If you really want to move the call near another command for some reason, than you could implement a DbCommandInterceptor .如果您出于某种原因确实想将呼叫移至另一个命令附近,则可以实现DbCommandInterceptor You could then parse the command text for a specific CREATE TABLE statement or something and then issue your own statement.然后,您可以解析特定CREATE TABLE语句或其他内容的命令文本,然后发出您自己的语句。 (If you want to go that route, post a comment and I update this answer with some code.) (如果你想 go 这条路线,发表评论,我用一些代码更新这个答案。)


In case you switch from using context.Database.EnsureCreated() to migrations, you can use the Sql() method to inject your own statements:如果你从使用context.Database.EnsureCreated()切换到迁移,你可以使用Sql()方法来注入你自己的语句:

public partial class MyMigration : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql("SELECT create_hypertable('conditions', 'time');")
    }
}

Maybe you can elaborate a bit, why you feel the need to move the create_hypertable() calls closer to their tables.也许您可以详细说明一下,为什么您觉得需要将create_hypertable()调用移近他们的表。


If you just care about having the create_hypertable() calls close in the C# code, but not necessarily in the generated SQL code, then introducing a custom attribute (or an interface) and a method to apply the hypertable to all entities that have properties decorated with this attribute (or implement this interface) might be good enough:如果您只关心在 C# 代码中关闭create_hypertable()调用,但不一定在生成的 SQL 代码中,然后引入自定义属性(或接口)和将超表应用于所有具有修饰属性的实体的方法使用此属性(或实现此接口)可能就足够了:

[AttributeUsage(AttributeTargets.Property)]
public class HypertableColumnAttribute : Attribute
{
}

public static class DbContextExtensions
{
    public static void ApplyHypertables(this Context context)
    {
        var entityTypes = context.Model.GetEntityTypes();

        foreach (var entityType in entityTypes)
        {
            foreach (var property in entityType.GetProperties())
            {
                if (property.ClrType.GetCustomAttribute(
                    typeof(HypertableColumnAttribute)) != null)
                {
                    var tableName = entityType.GetTableName();
                    var columnName = property.GetColumnName();

                    context.Database.ExecuteSqlInterpolated(
                        $"SELECT create_hypertable({tableName}, {columnName});");
                }
            }
        }
    }
}

public class IceCream
{
    public int IceCreamId { get; set; }
    public string Name { get; set; }

    [HypertableColumn]
    public DateTime? UpdatedAt { get; set; }
}

public class Context : DbContext
{
    public DbSet<IceCream> IceCreams { get; set; }

    /// ...
}

internal static class Program
{
    private static void Main()
    {
        using var context = new Context();

        context.Database.EnsureDeleted();
        context.Database.EnsureCreated();
        context.ApplyHypertables();

        var iceCreams = context.IceCreams
            .OrderBy(i => i.IceCreamId)
            .ToList();
        
        Debug.Assert(iceCreams.Count == 2);
    }
}

In case you want to keep your model classes clean of any database specific attributes, you can use annotations instead.如果您想保持 model 类没有任何数据库特定属性,则可以使用注释。 The idea is basically the same, but annotations are EF Core metadata and can be defined as part of an IEntityTypeConfiguration<T> implementation using the HasAnnotation() method:想法基本相同,但注释是 EF Core 元数据,可以使用HasAnnotation()方法定义为IEntityTypeConfiguration<T>实现的一部分:

public static class DbContextExtensions
{
    public static void ApplyHypertables(this Context context)
    {
        var entityTypes = context.Model.GetEntityTypes();

        foreach (var entityType in entityTypes)
        {
            foreach (var property in entityType.GetProperties())
            {
                var isHypertableColumn = property.FindAnnotation(MyAnnotationNames.Hypertable)?.Value;
                if ((bool)(isHypertableColumn ?? false))
                {
                    var tableName = entityType.GetTableName();
                    var columnName = property.GetColumnName();

                    context.Database.ExecuteSqlInterpolated(
                        $"SELECT create_hypertable({tableName}, {columnName});");
                }
            }
        }
    }
}

public static class MyAnnotationNames
{
    public const string Prefix = "MyPrefix:";
    public const string Hypertable = Prefix + "Hypertable";
}

public class IceCream
{
    public int IceCreamId { get; set; }
    public string Name { get; set; }
    public DateTime? UpdatedAt { get; set; }
}

public class IceCreamConfiguration : IEntityTypeConfiguration<IceCream>
{
    public void Configure(EntityTypeBuilder<IceCream> builder)
    {
        builder.Property(e => e.UpdatedAt)
            .HasAnnotation(MyAnnotationNames.Hypertable, true);
        
        builder.HasData(
            new IceCream {IceCreamId = 1, Name = "Vanilla"},
            new IceCream {IceCreamId = 2, Name = "Chocolate"});
    }
}

public class Context : DbContext
{
    public DbSet<IceCream> IceCreams { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
        => modelBuilder.ApplyConfiguration(new IceCreamConfiguration());

    /// ...
}

internal static class Program
{
    private static void Main()
    {
        using var context = new Context();

        context.Database.EnsureDeleted();
        context.Database.EnsureCreated();
        context.ApplyHypertables();

        var iceCreams = context.IceCreams
            .OrderBy(i => i.IceCreamId)
            .ToList();
        
        Debug.Assert(iceCreams.Count == 2);
    }
}

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

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