如何防止插入 DateTimeOffset 的默认值?

How can I prevent the default value of DateTimeOffset from being inserted?

我正在使用 Entity Framework Core 3.1.7 并创建了一个名为 Event 的实体,我是这样设置的:

public class Event
{
    public long Id { get; set; }
    public DateTimeOffset FirstOccurred { get; set; }
}

实体配置如下所示:

builder.Property(e => e.FirstOccurred)
    .IsRequired();

我使用我的 dbContext 来持久化实体,如下所示:

await dbContext.Events.AddAsync(new Event());

在这种情况下,我错误地预期会在数据库级别抛出异常,因为该值不能为空。

实际发生的是:实体很高兴地持久化 FirstOccurred 设置为 0001-01-01T00:00:00+00:00

这是有道理的,因为使用了默认值DateTimeOffset

现在我的问题是:如何改进我的设计以防止插入此默认值?

我已经有了一些想法:

  1. 保持上面的代码不变,但要确保在使用实体的任何地方,我都正确设置了值。缺点:不能保证这会随着时间的推移在团队中始终如一地应用。
  2. 使 DateTimeOffset 可为空,这在上面的 AddAsync() 调用中实际上会导致 SQL 异常。缺点:乍一看,DateTimeOffset? FirstOccurred 可能令人困惑,因为实际的数据库约束不允许 null
  3. FirstOccurred 删除 set; 并创建一个需要设置此 属性 的构造函数,例如new Event(DateTimeOffset.Now)

我认为你的最后一个想法是正确的。

Remove set; for FirstOccurred and create a constructor that requires this property to be set, e.g. new Event(DateTimeOffset.Now)

在没有时间戳的情况下跟踪事件没有意义,使用时间戳的默认值当然也没有意义。

更改您的模型以要求时间戳的值可确保您不会将默认数据写入记录,并防止在相应的 table 列不可为空时看到可为空的模型字段而造成混淆。

public class Event {
    public Event (DateTimeOffset firstOccurred) { FirstOcurred = firstOcurred; }
    public long Id { get; set; }
    public DateTimeOffset FirstOccurred { get; set; }
}

只是来自 documentation 的注释,不要删除 set; 访问器,如果您不想在构造后更改值,只需将其标记为 private

Once properties are being set via the constructor it can make sense to make some of them read-only. EF Core supports this, but there are some things to look out for:

Properties without setters are not mapped by convention. (Doing so tends to map properties that should not be mapped, such as computed properties.) Using automatically generated key values requires a key property that is read-write, since the key value needs to be set by the key generator when inserting new entities. An easy way to avoid these things is to use private setters.

当然,您也可以通过覆盖 属性.

的默认值来保持无参数构造函数的灵活性
public class Event {
    public long Id { get; set; }
    public DateTimeOffset FirstOccurred { get; set; } = DateTimeOffset.Now;
}