-
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
Update pattern for scaffolding column default constraints #13613
Comments
Struggling with the same issue. Its really not desirable to have "bool" in the DB and "bool?" in the code.
|
The most
It seems to be completely indiscriminate too. Verified was defined in the model correctly but is missing the default value in OnModelCreating whereas Active was defined incorrectly in the model but correctly in OnModelCreating. It also got the unique index, but ignored the primary key completely. Makes it impossible to use scaffolding in a build task without constantly fudging with the model. M(sometimes)VC. |
@christopher-landress @olavivaino With regard to the boolean default values, can you give some details on you wish these default values to be used from EF? Should they be included in the model, and, if so, why? @christopher-landress If a primary key is being ignored, can you post the details on the key that is being ignored--this sounds like a bug. |
The default value should be whatever the constraint specifies. In the above case Verified should be 0 and Active should be 1... specified by :
and
If NOT NULL & a default constraint are defined for a bit field, it should never be specified with a Boolean? type. I would argue that Boolean? should only be specified when the value could be null, but even then if there is a default it shouldn't be a nullable type. As far as the primary key is concerned, I would have expected to see the following: This one appears to be present :
I expected to see the following but it was missing :
This happens with the release version of the tools as well as the pre-release. Very simple to test with the above sql script. |
@christopher-landress Assume that the reverse engineering is doing what you expect, such that you have two bool properties in your model, both with HasDefaultValue (or HasDefaultValueSql) set. Can you describe how you will then use these properties? What will the default value do? Why is it there? (I'm not asking to be mean, but because how to deal with column defaults depends on what they are used for, so I'm trying to understand what your expectation is once the default gets into the model.) |
The way I see it, and maybe I am over/under thinking it, is the advantage of having default values for future use. Neither of which are initially important to the insert. Verified would be updated to true once the email address is validated, and Active would work as a soft delete for the record. This could absolutely be accomplished with different schema but I feel like that defeats the purpose of db first. I don't feel like it's unreasonable to set the initial state of the record without intervention, especially when those values aren't designed to be visible. Some of us like to stuff as much logic as we can into the database, assuming it can be used across multiple backends. What is the advantage of having to set Verified and Active explicitly before inserting when my schema is designed to set those values for me? I also use PostgreSQL, which supports schema inheritance. Having a base table that supports keys, timestamps, and soft deletes is very helpful if I can assume the values that get set are standardized. Perhaps I am missing your point. Maybe it's a disconnect between developers who like to stuff as much logic into the database as possible and those who don't. I'm having a hard time believing other people aren't having this same issue. |
@christopher-landress Consider this entity type: public class Foo
{
public int Id { get ;set; }
public bool IsValid { get ;set; }
} where IsValid has a column default of After inserting the the following entity instance, what would you expect the IsValid column value to be set to? context.Add(new Foo { IsValid = false });
context.SaveChanges(); Likewise, for this: context.Add(new Foo { IsValid = true });
context.SaveChanges(); And for this one: context.Add(new Foo());
context.SaveChanges(); |
I understand the limitation of the model's type.
Is there no way to fix this in OnModelCreating without using a nullable type? |
@christopher-landress We're open to ideas. |
Note for triage: thought about this some more last night and while this doesn't work now maybe we could make it work: public class Blog
{
public int Id { get; set; }
private bool? _isValid;
public bool IsValid
{
get => _isValid ?? false;
set => _isValid = value;
}
} modelBuilder
.Entity<Blog>()
.Property(e => e.IsValid)
.HasDefaultValue(true)
.UsePropertyAccessMode(PropertyAccessMode.Field); The idea is that the update pipeline reads from the backing field and so can tell whether or not an explicit value has been set. But the entity type still has a non-nullable property so application code doesn't need to be changed to handle this. |
Update following further discussion in triage. When we are able to parse the value of the default constraint, then we will use it to generate full client-side defaults. For example: public bool IsValid { get; set; } = true;
public int Lives { get; set; } = 3; These types will always insert some specific value, but if the value is not set explicitly, then it will match the value that would have been generated by the constraint. (Note that we effectively do this already when the constraint default is the same as the CLR default.) For cases where the constraint cannot be parsed, we will use the pattern from the previous comment. This will allow the store default to be used, although with more complex entity classes. |
I would prefer not to have default value in the model at all. In DB default value is mandatory for not null columns when adding column to table with existing DB. The main issue is that datatype in the model and in the DB are not matching anymore. |
@olavivaino Yes, that is a valid option, which is why I was asking how the defaults were going to be used in the model. I think we recognize the need for both of these cases, although it is not clear which should be the default. |
See further discussion here: #15070
|
Sorry for hijacking this issue, but I think it's the most relevant place. As I understand the discussion, EF Core should already properly map a non-null bool with a database default of (1) to true, not a nullable bool? I have an existing database (created with EF 6 Code First a few years ago) that I am now accessing from a new EF Core 3.1.3 based client. I'd like to have a clean scaffold, so that I can re-run when I do future migrations in the EF 6 code base. Relevant part of table definiton: CREATE TABLE [dbo].[Customers](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Options_HasMusicLicense] [bit] NOT NULL,
);
ALTER TABLE [dbo].[Customers] ADD DEFAULT ((1)) FOR [Options_HasMusicLicense] Command to scaffold: Resulting in: Generated class: public partial class Customer
{
[Key]
public int Id { get; set; }
[Required]
[Column("Options_HasMusicLicense")]
public bool? OptionsHasMusicLicense { get; set; }
} And in my model builder: modelBuilder.Entity<Customer>(entity =>
{
entity.Property(e => e.OptionsHasMusicLicense).HasDefaultValueSql("((1))");
}); I'd like the property on [Column("Options_HasMusicLicense")]
public bool OptionsHasMusicLicense { get; set; } = true; What am I doing wrong? 🤔 |
This has not been implemented yet. |
@ajcvickers I have been thinking about this in relation to ErikEJ/EFCorePowerTools#348 - would it be an option to do the below? (In CSharpEntityTypeGenerator) - or do you prefer to wait for this? #13613 (comment) private void GenerateRequiredAttribute(IProperty property)
{
if (!property.IsNullable
&& property.GetDefaultValue() != null
&& property.GetDefaultValueSql() != null
&& property.ClrType.IsNullableType()
&& !property.IsPrimaryKey())
{
_sb.AppendLine(new AttributeWriter(nameof(RequiredAttribute)).ToString());
}
} |
…as not been set Fixes #701 Part of #13224, #15070, #13613 This PR contains the underlying model and change tracking changes needed to support sentinel values. Future PRs will add model building API surface and setting the sentinel automatically based on the database default. There is a breaking change here: explicitly setting the value of a property with a temporary value no longer automatically makes the value non temporary.
…as not been set Fixes #701 Part of #13224, #15070, #13613 This PR contains the underlying model and change tracking changes needed to support sentinel values. Future PRs will add model building API surface and setting the sentinel automatically based on the database default. There is a breaking change here: explicitly setting the value of a property with a temporary value no longer automatically makes the value non temporary.
Part of #13613 This is a step closer to being able to scaffold bool columns with default values without using a nullable property or backing field.
Just to add another case which uses the Postgres boolean type
then the scaffolded db context contains this code for the column
The notion of this column having a default value is lost when scaffolding. When doing an initial migration from this kind of db context the notion of default value is lost there too obviously. It's the same if I scaffold using --data-annotations. |
I have manually configured defaults in the model and now there is a warning of
which doesn't make a lot of sense because it is obvious that I don't understand why the obsession with nullable boolean and the need to display warnings here. |
I believe the confusion comes in because some may want the database default value to hold special meaning in the EF model object. I personally really don't have any expectation of that sort. The default value is for migrations so the database knows what value to use when I add the new Boolean column and for 3rd party non-EF software to use. I just expect EF to be able to create/migrate the default value constraint and otherwise ignore it. I don't try to conflate the database default value to having any effect on the C#/EF property at all. If I "new" that class, it won't necessarily initialize to the value of the database default value. In SQL if you have a bit (same for int/varchar), if you do the equivalent: the database does not say oh, he's inserting the default value into mycol, but that column has a default value of 1, then he meant to insert a 1. Nor will it accept it if you specify NULL in that column since that column isn't nullable. However, if you do the equivalent: I see these as two separate things. If you could do the following: Of course, if I generate my model, and then through another tool (SSMS) add a bool column and assign a default value, and my code continues to use the old model, EF will work as I expect. It will insert rows into the table and use the default value. I hope that doesn't make things even less clear. |
Part of #13613 This is a step closer to being able to scaffold bool columns with default values without using a nullable property or backing field. Updated to: - Move parsing to time database model is created - Integrate with parsing for CLR defaults - Updated code generation so that it is only used by scaffolding, not migrations
EF Core 8 still issues this warning at runtime
for the following property
As before I still don't understand why the warning - what it is that we should be warned about in this case - don't get it. If those warnings weren't removed from the new EF version, is there at least an option to disable them? |
When using database first, creating a table (MSSQL) with a a bit field that has a default value produces strange results.
MSSQL Script:
Scaffolding command:
Scaffold-DbContext "Server=172.16.1.140;Database=DocStudio;User Id=<uid>;Password=<pass>;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -f
Generated model :
and finally, the fluent OnModelCreating method:
Several issues here. Note the difference between Verified and Active. Despite being identical in their definition they produce different property types in the model.
The lack of a default value for Verified. There should be a definition like so:
Missing a primary key definition.
Finally a warning for Active, but not for Verified:
The text was updated successfully, but these errors were encountered: