[英]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?创建表后发出命令的最佳方法是什么?
EnsureCreated()
, using ExecuteSqlRaw()
?EnsureCreated()
之后,使用ExecuteSqlRaw()
吗? That means all the tables have been created before the SQL is executed.DbContext
? DbContext
内部的某个地方?IEntityTypeConfiguration<>
?IEntityTypeConfiguration<>
? 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.