简体   繁体   English

PostgreSQL 的 Entity Framework Core 代码第一个默认值

[英]Entity Framework Core code first default values for PostgreSQL

So far I read the docs, tutorials google brought up and other SO questions but it seems I miss something and I don't get it (to work).到目前为止,我阅读了谷歌提出的文档、教程和其他 SO 问题,但似乎我错过了一些东西并且我没有得到它(工作)。

I try to implement a really tiny PostgreSQL database for a .NET Core 2.1 web api (microservice).我尝试为 .NET Core 2.1 web Z8A5DA52ED126447D359E70C057 实现一个非常小的 PostgreSQL 数据库。 I am used to the database first approach but for this service I decided to give the code first approach a try.我习惯了数据库优先的方法,但对于这项服务,我决定尝试使用代码优先的方法。

I also decided to use the fluent api of the ModelBuilder to keep the classes clean from attributes and define most of the structure in the DbContext.OnModelCreating method.我还决定使用 ModelBuilder 的流畅 api 来保持类与属性无关,并在 DbContext.OnModelCreating 方法中定义大部分结构。 Andy maybe one time find a solution how to keep the DbContext clean from Postgres specific elements and move them to the migrations...安迪也许有一次会找到一种解决方案,如何让 DbContext 从 Postgres 特定元素中保持干净,并将它们移动到迁移中......

My problem is that the requirements define a lot of default values and I can't get them working as they should behave.我的问题是需求定义了很多默认值,我无法让它们按应有的方式工作。

For example I have the table Entity1 which just has the 3 columns:例如,我有表 Entity1,它只有 3 列:

  • int Id (auto incrementing id) int Id(自动递增 id)
  • bool?布尔? IsEnabled (which should get the default value true) IsEnabled(应该获得默认值 true)
  • DateTime?约会时间? LastStatusChange (timestamp without timezonewhich should get set on create or update to default value CURRENT_TIMESTAMP) LastStatusChange(没有时区的时间戳,应该在创建或更新为默认值 CURRENT_TIMESTAMP 时设置)

The idea behind the default value for the timestamp is to have the database being the single instance creating timestamps and using the db default values as configuration for all running instances.时间戳默认值背后的想法是让数据库成为创建时间戳的单个实例,并使用 db 默认值作为所有正在运行的实例的配置。 Eg.例如。 when requirements change to "the default value should now be false" we just change the db default values in the existing databases and update the migration for newer installments.当需求更改为“默认值现在应该为 false”时,我们只需更改现有数据库中的 db 默认值并更新迁移以获取更新的分期付款。

My modelBuilder code currently is:我的modelBuilder代码目前是:

  modelBuilder.Entity<Entity1>(entity =>
  {
    entity.HasKey(e => e.Id);

    entity.Property(e => e.Id)
      .ValueGeneratedOnAdd();

    entity.Property(e => e.IsEnabled)
      .HasDefaultValue(true)
      .ValueGeneratedOnAddOrUpdate();

    entity.Property(e => e.LastStatusChange)
      .HasColumnType("timestamp without time zone")
      .HasDefaultValueSql("CURRENT_TIMESTAMP")
      .ValueGeneratedOnAddOrUpdate();
  });

Which works for new created values.这适用于新创造的价值。

When toggling or resetting the fields i use切换或重置我使用的字段时

entity.LastStatusChange = null;

and or和或

entity.IsEnabled = null;

I assumed setting them to null would trigger the creation of a default value but this does not affect the LastStatusChange field (the value stays the same) and sets IsEnabled to null.我假设将它们设置为 null 会触发创建默认值,但这不会影响 LastStatusChange 字段(值保持不变)并将 IsEnabled 设置为 null。

Anyway to get db default values on update via entity framework core?无论如何通过实体框架核心获取数据库默认值更新?

As the EF Core docs mention , HasDefaultValue() and HasDefaultValueSql() only specify the value that gets set when a new row is inserted.正如EF Core 文档所述HasDefaultValue()HasDefaultValueSql()仅指定插入新行时设置的值。 Setting a column to null is a completely different thing (after all, null is a valid value for those columns).将列设置为 null 是完全不同的事情(毕竟,对于这些列,null 是有效值)。 You may be looking for computed columns , which is a different feature.您可能正在寻找计算列,这是一个不同的功能。

Unfortunately, as the Npgsql docs mention , computed columns aren't supported by PostgreSQL at this time.不幸的是,正如Npgsql 文档所提到的,PostgreSQL 目前不支持计算列。 It's possible to set this up using PostgreSQL database triggers , but this isn't managed by EF Core so you'd have to use SQL in your migrations to set this up.可以使用PostgreSQL 数据库触发器进行设置,但这不是由 EF Core 管理的,因此您必须在迁移中使用 SQL 进行设置。

Solution for CreateDate in PostgreSQL: PostgreSQL 中 CreateDate 的解决方案:

builder.Property(e => e.CreationDate)
  .HasColumnType("timestamp without time zone")
  .HasDefaultValueSql("NOW()")
  .ValueGeneratedOnAdd();

Unfortunately there is not solution for update event.不幸的是,没有更新事件的解决方案。

I was able to solve the DateUpdated problem using nuget Triggered as neither [DatabaseGenerated(DatabaseGeneratedOption.Computed)] nor HasComputedColumnSql("timezone('utc', now()) nor ValueGeneratedOnAddOrUpdate() were working for postgres.我能够使用nuget Triggered解决 DateUpdated 问题,因为[DatabaseGenerated(DatabaseGeneratedOption.Computed)]HasComputedColumnSql("timezone('utc', now())ValueGeneratedOnAddOrUpdate()都没有为 postgres 工作。

The cool thing is that it works even for base class properties, all relevant entities are deriving from it很酷的是,它甚至适用于基本 class 属性,所有相关实体都源自它

public abstract class Auditable
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public DateTime DateCreated { get; private set; }

    public DateTime DateUpdated { get; set; }
}

public class User : Auditable
{
    [Key]
    public int Id { get; set; }
    ...
}

Here is how I did it:这是我的做法:

public class SetDateUpdated : IBeforeSaveTrigger<Auditable>
{
    public Task BeforeSave(ITriggerContext<Auditable> context, CancellationToken cancellationToken)
    {
        if (context.ChangeType == ChangeType.Modified)
        {
            context.Entity.DateUpdated = DateTime.UtcNow;
        }

        return Task.CompletedTask;
    }
}

In Program.cs register for DI:在 Program.cs 中为 DI 注册:

builder.Services.AddTransient(typeof(SetDateUpdated));

And finally:最后:

builder.Services.AddDbContext<DatabaseContext>
(
    options =>
    {
        options.UseNpgsql(builder.Configuration.GetConnectionString("DBConnection"));
        options.UseTriggers(triggerOptions =>
        {
            triggerOptions.AddTrigger<SetDateUpdated>();
        });

    }, ServiceLifetime.Transient, ServiceLifetime.Transient
);

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

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