dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.73k stars 3.17k forks source link

Attribute Code First project not apply correctly #20864

Closed arnaudhelin closed 2 years ago

arnaudhelin commented 4 years ago

Description of the issue

The decorating attributes in the model do not apply correctly. The [AllowNull] do not change the property correctly. The field in the schema database still stay as NotNull. By the way, the same odd behavior appears in other field on which the [Required] attribute do not apply.

Steps to reproduce

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["myproject.csproj", ""]
RUN dotnet restore "./myproject.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "myproject.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "myproject.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", myproject.dll"]
[Required]
[Column("Email")]
[EmailAddress]
[Display(Name = "Email")]
public string ProxyAddresses { get; set; }

[Column("BirthDate", TypeName = "DateTime2")]
[Display(Name = "Birthdate")]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
public DateTime BirthDate { get; set; }

[Column("AccountExpires", TypeName = "DateTime2")]
[Display(Name = "Expiration Date")]
[AllowNull]
[MaybeNull]
public DateTime AccountExpires { get; set; }

The ProxyAddress property is correctly set to not allow Null (or allow null if I remove [Required] attribute). But for the others, it does not work at all, seems like it is completely skipped. Even if I add [AllowNull] and [MaybeNull], the SSMS shows me that properties are not allowed to be null.

Exceptions

During the build, I have some exceptions, but I can't determine why I get these now.

Exception thrown: 'System.ObjectDisposedException' in System.Net.Sockets.dll
Exception thrown: 'Microsoft.Data.SqlClient.SqlException' in Microsoft.Data.SqlClient.dll
Exception thrown: 'Microsoft.Data.SqlClient.SqlException' in Microsoft.Data.SqlClient.dll
Exception thrown: 'Microsoft.Data.SqlClient.SqlException' in Microsoft.Data.SqlClient.dll
Exception thrown: 'Microsoft.Data.SqlClient.SqlException' in Microsoft.Data.SqlClient.dll
Exception thrown: 'Microsoft.Data.SqlClient.SqlException' in Microsoft.Data.SqlClient.dll
Exception thrown: 'Microsoft.Data.SqlClient.SqlException' in Microsoft.Data.SqlClient.dll
Exception thrown: 'Microsoft.Data.SqlClient.SqlException' in Microsoft.Data.SqlClient.dll
Exception thrown: 'Microsoft.Data.SqlClient.SqlException' in Microsoft.Data.SqlClient.dll
Exception thrown: 'Microsoft.Data.SqlClient.SqlException' in Microsoft.Data.SqlClient.dll
Exception thrown: 'Microsoft.Data.SqlClient.SqlException' in Microsoft.Data.SqlClient.dll
Exception thrown: 'Microsoft.Data.SqlClient.SqlException' in Microsoft.Data.SqlClient.dll
Exception thrown: 'Microsoft.Data.SqlClient.SqlException' in Microsoft.EntityFrameworkCore.Relational.dll
Exception thrown: 'System.DllNotFoundException' in Microsoft.AspNetCore.Cryptography.Internal.dll

I wonder if I had to install the Cryptography dll missing by myself (nuget directly) but it is written below the package, that it is recommended to not add the reference directly.

T-SQL create by EF (Use dbforge by Devart)

CREATE TABLE myproject.dbo.[User] (
  Id INT IDENTITY
 ,ObjectSid NVARCHAR(MAX) NULL
 ,SamAccountName NVARCHAR(MAX) NULL
 ,Firstname NVARCHAR(MAX) NOT NULL
 ,Lastname NVARCHAR(MAX) NOT NULL
 ,Mobile NVARCHAR(MAX) NULL
 ,Phone NVARCHAR(MAX) NULL
 ,Email NVARCHAR(MAX) NOT NULL
 ,BirthDate DATETIME2 NOT NULL
 ,AccountExpires DATETIME2 NOT NULL
 ,DistinguishedName NVARCHAR(MAX) NULL
 ,fk_ManagerId INT NULL
 ,fk_ProfileId INT NULL
 ,fk_AddressId INT NULL
 ,CONSTRAINT PK_User PRIMARY KEY CLUSTERED (Id)
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

CREATE INDEX IX_User_fk_AddressId
ON myproject.dbo.[User] (fk_AddressId)
ON [PRIMARY]
GO

CREATE INDEX IX_User_fk_ManagerId
ON EZIam.dbo.[User] (fk_ManagerId)
ON [PRIMARY]
GO

CREATE INDEX IX_User_fk_ProfileId
ON myproject.dbo.[User] (fk_ProfileId)
ON [PRIMARY]
GO

ALTER TABLE myproject.dbo.[User]
ADD CONSTRAINT FK_User_Address_fk_AddressId FOREIGN KEY (fk_AddressId) REFERENCES dbo.Address (Id)
GO

ALTER TABLE myproject.dbo.[User]
ADD CONSTRAINT FK_User_Profile_fk_ProfileId FOREIGN KEY (fk_ProfileId) REFERENCES dbo.Profile (Id)
GO

ALTER TABLE myproject.dbo.[User]
ADD CONSTRAINT FK_User_User_fk_ManagerId FOREIGN KEY (fk_ManagerId) REFERENCES dbo.[USER] (Id)
GO

As you can see there is a NOT NULL value which should not be there.

Further technical details

EF Core version: 3.1.3 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: netcoreapp3.1 Operating system: Windows 10 Pro Insider Preview (build 19592) IDE: Visual Studio Community 2019 16.5.0

ajcvickers commented 4 years ago

@arnaudhelin EF doesn't currently read the AllowNull or MaybeNull attributes. EF interprets Required to mean make the column non-nullable, the same as if you specify Required in the model building API.

roji commented 4 years ago

@arnaudhelin you seem to be applying [AllowNull] and [MaybeNull] to DateTime, which is a value type - these attributes are meant for use only for reference types (they were introduced in C# 8 for the new nullable reference types feature). Just as in C# the compiler doesn't make your property nullable because of these attributes, EF Core doesn't respect them either.

For value types like DateTime, you can make the property nullable in C# by specifying DateTime?.

arnaudhelin commented 4 years ago

Thank you both. Effectively I tried with the ? and it works.

Regards