EF Core 3.1 调用 context.Add() 在使用 SQL 中的序列的模型上抛出 "nullable object must have a value"

EF Core 3.1 calling context.Add() throws "nullable object must have a value" on models that utilize sequences in SQL

我正在尝试将 asp.net 核心项目从 2.2 迁移到 3.1,因此我也必须从 EF Core 2.2 升级到 EF Core 3.1。

我尝试直接升级 2.2 项目并失败,然后创建了一个新项目,复制了我的模型和 DbContext,现在在任何调用 MyDbContext.Add() 时出现 "nullable object must have a value" 错误使用 SQL 中的序列生成一些值的模型。

任何不使用序列的模型都不会发生此错误,我可以在这些模型上调用 add 就好了。

我尝试使用的模型看起来像这样:

(请注意,我已重命名每个 属性 并删除了多次使用的数据类型)

public class MyModel
    {
        public int Id { get; set; }

        private DateTime? timeStamp;
        public DateTime TimeStamp
        {
            get { return timeStamp ?? DateTime.UtcNow; }
            set { timeStamp = value; }
        }

        public int MySequence { get; private set; }
        public string MySequenceWithPrefix
        {
            get
            {
                return "Prefix" + MySequence.ToString()
            }
        }

        public MyEnumType EnumType { get; set; }

        public string StringValue { get; set; }

        public bool? NullableBoolean { get; set; }
        public bool NotNullableBoolean { get; set; }

        public int? NullableFK { get; set; } //For the below navigation property
        public MyNavigationProperty NavigationProperty { get; set; } 

        public ICollection<MyCollectionOfThings> Things { get; set; }

        public DateTime? NullableDateTime { get; set; }

        public MyNullableEnumProperty? NullableEnumProperty { get; set; }
        [DataType(DataType.Currency)] public double? NullableCurrencyField { get; set; }

    }

我在模型构建器中配置了这样的序列:

modelBuilder.HasSequence<int>("MySequence")
                .StartsAt(40000)
                .IncrementsBy(1);

modelBuilder.Entity<MyModel>()
                .Property(model => model.MySequence)
                .HasDefaultValueSql("NEXT VALUE FOR MySequence");

我正在尝试这样调用 MyDbContext.Add():

MyModel testMyModel = new MyModel()
{
    //set non-nullable fields here
};

//throws "Nullable object must have a value."
MyDbContext.Add(testMyModel);

我不确定从 EF Core 2.2 到 3.1 是否有涉及序列的重大变化,或者我在创建新项目和手动移动模型时是否设置不正确。

如果您需要任何进一步的信息,请告诉我。

编辑: 带有堆栈跟踪的完整错误消息是这样的:

InvalidOperationException: Nullable object must have a value.

    System.Nullable<T>.get_Value()
    lambda_method(Closure , InternalEntityEntry )
    Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry+OriginalValues..ctor(InternalEntityEntry entry)
    Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.EnsureOriginalValues()
    Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntrySubscriber.SnapshotAndSubscribe(InternalEntityEntry entry)
    Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry entry)
    Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, bool acceptChanges, bool modifyProperties)
    Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState entityState, bool acceptChanges, bool modifyProperties, Nullable<EntityState> forceStateWhenUnknownKey)
    Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.PaintAction(EntityEntryGraphNode<ValueTuple<EntityState, EntityState, bool>> node)
    Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityEntryGraphIterator.TraverseGraph<TState>(EntityEntryGraphNode<TState> node, Func<EntityEntryGraphNode<TState>, bool> handleNode)
    Microsoft.EntityFrameworkCore.ChangeTracking.Internal.EntityGraphAttacher.AttachGraph(InternalEntityEntry rootEntry, EntityState targetState, EntityState storeGeneratedWithKeySetTargetState, bool forceStateWhenUnknownKey)
    Microsoft.EntityFrameworkCore.DbContext.SetEntityState(InternalEntityEntry entry, EntityState entityState)
    Microsoft.EntityFrameworkCore.DbContext.SetEntityState<TEntity>(TEntity entity, EntityState entityState)
    Microsoft.EntityFrameworkCore.DbContext.Add<TEntity>(TEntity entity)
MyProject.Areas.MyArea.Controllers.MyController.Form(FormValues values) in MyController.cs

                     MyDbContext.Add(testMyModel);

Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor+TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, object controller, object[] arguments)
System.Threading.Tasks.ValueTask<TResult>.get_Result()
System.Runtime.CompilerServices.ValueTaskAwaiter<TResult>.GetResult()
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

如果堆栈跟踪太长,请提前致歉。我不知道你需要什么信息。

问题出在您的 TimeStamp 属性 上。尝试用 NotMappedAttribute 标记它,错误就会消失。我无法解释是什么内部 EF 魔法使它以这种方式运行,但我个人强烈建议不要在 EF 模型中使用 "non-trivial" 属性,例如您的 TimeStamp 模型。

UPD

似乎 EF 认为 private DateTime? timeStamp;TimeStamp 属性 的支持字段,尽管 documentation saying that backing filed should start with _ or m_, but 回答指出驼峰式属性也有效,并且重命名 timeStamptimeStampss 之类的东西也会使错误消失。

另见 this breaking change for backing fields in 3.0