Skip to content
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

Remaining reference nullability work #16440

Closed
divega opened this issue Jul 4, 2019 · 18 comments
Closed

Remaining reference nullability work #16440

divega opened this issue Jul 4, 2019 · 18 comments
Assignees
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-enhancement
Milestone

Comments

@divega
Copy link
Contributor

divega commented Jul 4, 2019

We are going to use this issue to track any additional changes necessary to properly support nullable reference types in 3.0, in particular react to any API changes to the C# 8.0 compiler generated attributes and support for custom attributes.

@roji
Copy link
Member

roji commented Jul 4, 2019

Some first things that we need to look into:

Preconditions and postconditions ([AllowNull], [DisallowNull], [MaybeNull], [NotNull], see https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-05-15.md). These can go on:

  • (Generic) properties
  • Property getters and setters. We can probably ignore these as have the nullability of the property itself (although I'm not sure what it means to have a nullable property with a getter and setter that are nullable).
  • [NullablePublicOnly] on the module. This can tell us that the model was compiled without metadata on non-public members. We may want to emit a warning if we see a mapped, non-public property in a module without non-public metadata. The API shape for this attribute is being finalized.

@ajcvickers ajcvickers added this to the 3.0.0 milestone Jul 5, 2019
@sjb-sjb
Copy link

sjb-sjb commented Jul 23, 2019

Question, is there a document or discussion that explains how "string" vs "string?" will be treated by EF Core once non-nullable reference types are available, and how the [Required] attribute will be interpreted for each of them?

@roji
Copy link
Member

roji commented Jul 23, 2019

@sjb-sjb we don't have anything documented yet AFAIK, but if you opt into the nullable reference types feature, non-nullable types (e.g. string) will be treated as if they have [Required] on them. Placing an explicit [Required] attribute will override that allow you to manually specify whether a column in the database is nullable or not.

@sjb-sjb
Copy link

sjb-sjb commented Jul 24, 2019

Do you mean that if I have string? then I can also add [Required] to force it to be non-nullable in the database -- in other words if I opt in to non-nullable reference types then "[Required] string?" and just "string" would be treated the same way? In that case the meaning of just plain "string" would change in both the EF database mapping and in C# when I opt into non-nullable reference types -- which would make sense.

@roji
Copy link
Member

roji commented Jul 24, 2019

if I opt in to non-nullable reference types then "[Required] string?" and just "string" would be treated the same way

That's correct - as far as EF and the database are concerned. A non-nullable database column would be created in migrations, and EF would expect to never see a null when writing entities.

@ajcvickers
Copy link
Member

@roji To find link to remaining change.

@roji
Copy link
Member

roji commented Jul 29, 2019

Possible (but not certain) further changes in the nullability attributes: https://github.com/dotnet/corefx/issues/36222#issuecomment-511973898.

Apart from that there's also pre- and post-conditions.

@roji roji changed the title React to further changes in nullable reference types in 3.0 Remaining reference nullability work Jul 29, 2019
@roji
Copy link
Member

roji commented Jul 29, 2019

Got confirmation that no further changes to nullability metadata. Keeping open to track pre- and post-conditions.

roji added a commit that referenced this issue Jul 31, 2019
As per dotnet/roslyn#37610, the compiler will
no longer emit nullability context attributes at the module level,
only at the type and method level.

Simplify our non-nullability detection convention.

Part of #16440
@roji
Copy link
Member

roji commented Jul 31, 2019

Note: the nullability context attribute will no longer appear at the module level (dotnet/roslyn#37610), so simplified our convention to not handle that.

@sjb-sjb
Copy link

sjb-sjb commented Jul 31, 2019

How would I specify a string that cannot be null but which can be empty ("")? If non-nullable string is interpreted by EF in the same way as [Required] string? then it would have to be a string of length at least one. Possibly-empty but non-null strings could be desirable for eliminating a lot of null-pointer tests after materialization.

@ajcvickers
Copy link
Member

@sjb-sjb I think you're confusing what Required does as a validation attribute with how it is interpreted by EF mapping. In EF mapping required only influences nulability.

@sjb-sjb
Copy link

sjb-sjb commented Jul 31, 2019

Got it, thanks.

roji added a commit to roji/efcore that referenced this issue Aug 1, 2019
As per dotnet/roslyn#37610, the compiler will
no longer emit nullability context attributes at the module level,
only at the type and method level.

Simplify our non-nullability detection convention.

Part of dotnet#16440
roji added a commit that referenced this issue Aug 6, 2019
As per dotnet/roslyn#37610, the compiler will
no longer emit nullability context attributes at the module level,
only at the type and method level.

Simplify our non-nullability detection convention.

Part of #16440
roji added a commit that referenced this issue Aug 6, 2019
@roji
Copy link
Member

roji commented Aug 6, 2019

Here is a note regarding pre- and post-conditions (see these docs). In a nutshell, there are four attributes that allow you to override the default nullability setting on a get/set basis (e.g. a non-nullable property can still return or be set to null).

Within the EF Core context, I thought about it a little bit and came up with the following spec.

Scenario Result
Non-nullable property with [MaybeNull] Thje property may return nulls, so the EF property must be nullable. This means we will be writing nulls into the property when materializing, violating the property's contract (doesn't seem to be an issue).
Non-nullable property with [AllowNull] The property allows setting null but will never return null, so the EF property should be non-nullable. Useful for properties that support setting null but translate internally it to some non-null value (e.g. empty string).
Nullable property with [NotNull] The property will never return null, but the explicit nullability expresses user intent, so the EF property should be nullable
Nullable property with [DisallowNull] The property doesn't allow setting null, but can return null, so the property must be nullable.

According to the table above, the only attributes EF actually needs to recognize is [MaybeNull], since it means that a non-nullable property may actually return nulls (which need to be written to the database).

This logic is implemented in #16985, but it would be good to confirm the above.

@roji
Copy link
Member

roji commented Aug 6, 2019

Following the pre- and post-conditions discussed above, we also need to take care of backing fields - the current nullability implementation looks only at the CLR property, but a backing field may be configured with differing nullability. It seems like we should look at the configured PropertyAccessMode, and examine the appropriate member (field or property). FieldDuringConstruction should also examine the property's nullability rather than the field.

Decision: like with other modelling aspects, we only consult the public-facing property, so the field's reference nullability will not be taken into account when deciding on the EF property's requiredness. If only a field is configured without a property, the field's nullability will of course be the determining factor. This means that users can make EF aware only of the field, and do whatever they want with the property.

roji added a commit that referenced this issue Aug 6, 2019
@roji roji added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Aug 6, 2019
@ajcvickers
Copy link
Member

@roji Agree with your analysis/decisions on the pre/post conditions.

roji added a commit that referenced this issue Aug 7, 2019
As per dotnet/roslyn#37610, the compiler will
no longer emit nullability context attributes at the module level,
only at the type and method level.

Simplify our non-nullability detection convention.

Part of #16440
roji added a commit that referenced this issue Aug 7, 2019
@roji
Copy link
Member

roji commented Aug 7, 2019

Closing as [MaybeNull] has been handled in #16985, and we've decided not to look at backing fields for the purpose of requiredness.

@roji roji closed this as completed Aug 7, 2019
@ajcvickers ajcvickers modified the milestones: 3.0.0, 3.0.0-preview9 Aug 21, 2019
@ajcvickers ajcvickers modified the milestones: 3.0.0-preview9, 3.0.0 Nov 11, 2019
@sjb-sjb
Copy link

sjb-sjb commented Aug 13, 2020

@ajcvickers, on your note that [Required] on a nullable property causes EF to make the database field non-nullable .... does it have to be RequiredAttribute or could it be an attribute derived from RequiredAttribute ?

@ajcvickers
Copy link
Member

@sjb-sjb Might work with a derived type. Have you tried it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. type-enhancement
Projects
None yet
Development

No branches or pull requests

4 participants