I'm using Entity Framework Core 3.1.7 and created an entity called Event
, which I set up like this:
public class Event
{
public long Id { get; set; }
public DateTimeOffset FirstOccurred { get; set; }
}
The entity configuration looks like this:
builder.Property(e => e.FirstOccurred)
.IsRequired();
I use my dbContext
to persist the entity like this:
await dbContext.Events.AddAsync(new Event());
In this scenario, I was incorrectly expecting that an exception would be thrown at the Database level because the value can't be null.
What actually happens is: the entity is happily persisted with FirstOccurred
set to 0001-01-01T00:00:00+00:00
This makes sense, because the default value of DateTimeOffset
is used.
Now my question: How could I improve my design to prevent this default value from being inserted?
Some ideas I had already:
DateTimeOffset
nullable, which in the above AddAsync()
call would actually cause an SQL exception. Downside: At first glance, DateTimeOffset? FirstOccurred
might be confusing because the actual DB constraints don't allow nullset;
for FirstOccurred
and create a constructor that requires this property to be set, e.g. new Event(DateTimeOffset.Now)
I think you're on the right track with your last idea.
Remove
set;
forFirstOccurred
and create a constructor that requires this property to be set, e.g.new Event(DateTimeOffset.Now)
It doesn't make sense to track an Event without the timestamp and it certainly doesn't make sense to use the default value for the timestamp.
Changing your model to require a value for the timestamp ensures that you are not writing default data to the record and prevents confusion from seeing a nullable model field when the corresponding table column is non-nullable.
public class Event {
public Event (DateTimeOffset firstOccurred) { FirstOcurred = firstOcurred; }
public long Id { get; set; }
public DateTimeOffset FirstOccurred { get; set; }
}
Just a note from the documentation, don't remove the set;
accessor, just mark it private
if you don't want the value to change after construction.
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.
Of course, you could also maintain the flexibility of the parameter-less constructor by overriding the default value for the property.
public class Event {
public long Id { get; set; }
public DateTimeOffset FirstOccurred { get; set; } = DateTimeOffset.Now;
}