-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Non-nullable bool column with default constraint #8777
Conversation
default constraint then a) map it to a nullable bool property and b) issue a warning message.
/// A column would be mapped to a bool type, is non-nullable and has a default constraint. | ||
/// This event is in the <see cref="DbLoggerCategory.Scaffolding" /> category. | ||
/// </summary> | ||
public static readonly EventId NonNullableBoooleanColumnHasDefaultConstraintWarning = MakeScaffoldingId(Id.NonNullableBoooleanColumnHasDefaultConstraintWarning); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are not logging DiagnosticSource
event then where are these ids used?
cc: @ajcvickers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ajcvickers for response to this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still the event ID for the ILogger message.
{ | ||
if (forceNullable) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move warning outside of this if block & right after checking conditions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could. Don't feel it makes make much difference.
@@ -310,8 +310,14 @@ protected virtual PropertyBuilder VisitColumn([NotNull] EntityTypeBuilder builde | |||
} | |||
|
|||
var clrType = typeScaffoldingInfo.ClrType; | |||
if (column.IsNullable) | |||
var forceNullable = typeof(bool) == clrType && column.DefaultValue != null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create variable nullable taking value from Column.IsNullable & update the variable if conditions match (in same block as log message)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a relatively minor point, but I think the way it is now is much clearer. I think it's good for the developer to know that this is happening because nullability is being forced by the scaffolding, rather than it being any intrinsic property of the underlying database model. By saying forceNullable
you are explaining that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its minor point but no need to compute same thing multiple times, if we are having extra variable then why not use it in well way. We start with value in database, we update the variable with whatever conditions comes up and we set nullability based on that. Today its 2 conditions, it may expand more in future. Its better to right a condensed code. We don't need variables to explain what is happening. Logging message would make it very clear why we decided to introduce nullability. And if still doesn't look very clear, feel free to write a comment for explanation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You still need to issue the warning message only if you are forcing nullability - and not just because something is nullable
- so you will need the forceNullable
variable (or at least an if
statement with the same logic). I could create forceNullable
and then immediately create nullable
as well to do what you want. If the variable name was something like wasNullableInDatabaseOrNullabilityWasForced
I'd be OK with it. But that's ridiculously long. Personally I find the other way much clearer so I'm going to stick with it for now.
@@ -361,7 +367,7 @@ protected virtual PropertyBuilder VisitColumn([NotNull] EntityTypeBuilder builde | |||
|
|||
if (!column.PrimaryKeyOrdinal.HasValue) | |||
{ | |||
property.IsRequired(!column.IsNullable); | |||
property.IsRequired(!column.IsNullable && !forceNullable); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would be just !nullable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment above - I think this is better.
public partial class NonNullBoolWithDefault | ||
{ | ||
public int Id { get; set; } | ||
public bool? TestWithDefault { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BooleanWithDefaultValueSql
and likewise for other property & classname
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. Don't think it's important as the test already explains that but OK. Why the Sql
addition?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just because it is Bool property with DefaultValueSql set. Becomes more explicit and easy to evaluate the expected output file as in fluent API, you can check if the property has HasDefaultValueSql generated. It's not important but being more explicit in test not gonna hurt and helps person to read the tests more easily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought bool
was obvious a) from the name of the test and b) from the produced type. And actually HasDefaultValueSql()
is the choice of the scaffolder - it could (and did in the past) do something different if it wanted. The fact that the underlying database model has ColumnModel.DefaultValue
set is what's being tested. But OK - like I say I don't think it really matters.
/// </summary> | ||
public static readonly EventDefinition<string> LogNonNullableBoooleanColumnHasDefaultConstraint | ||
= new EventDefinition<string>( | ||
DesignEventId.NonNullableBoooleanColumnHasDefaultConstraintWarning, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Used here.
@@ -584,6 +584,18 @@ public static string ExistingFiles([CanBeNull] object outputDirectoryName, [CanB | |||
DesignEventId.ForeignKeyPrincipalEndContainsNullableColumnsWarning, | |||
_resourceManager.GetString("LogForeignKeyPrincipalEndContainsNullableColumns"))); | |||
|
|||
/// <summary> | |||
/// The column '{columnName}' would normally be mapped to a non-nullable bool property, but it has a default constraint. Such a column is mapped to a nullable bool property to allow a difference between setting the property to false and invoking the default constraint. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@divega fwlink this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lajones - wait for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ajcvickers @smitpatel @divega are we doing a fwlink within the warning message? I didn't see a response from @divega
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lajones I will have it in a couple of minutes. Trying to pick a temporary landing page for it until we have the actual topic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, @divega. I'll add this to the message and check in.
@smitpatel @ajcvickers I think I've addressed everything. So I'm going to go ahead and check in soon. Let me know if I shouldn't. |
Also can you rebase on latest dev, both CI services are red. |
@smitpatel Sure. Np. |
Checked in with commit 61650f8. |
Excuse me, can I do something to disable this behavior? I'm trying to re-scaffold a database context for an existing project from an existing database (schema manually managed / updated by SQL) after upgrading to ASP.NET Core 2.0 but suddenly EF Core puts EDIT: oh lmao #9627 😕 |
Fix for #8400.
If Reverse Engineering would map a non-nullable column to a non-nullable bool property but the column has a default constraint then a) map it to a nullable bool property instead and b) issue a warning message.
This is to allow the user of the generated classes to distinguish between actively setting the property to 'false' and not setting the property and allowing the default constraint to generate the value in the database.