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

Viewed 560

I am trying to migrate an asp.net core project from 2.2 to 3.1 so I had to upgrade from EF Core 2.2 to EF Core 3.1 as well.

I tried and failed to upgrade the 2.2 project directly and made a new project, copied over my models and DbContext, and now am getting a "nullable object must have a value" error when calling MyDbContext.Add() on any models that use a sequence in SQL to generate some value.

This error does not occur for any models that do not utilize sequences and I can call add on those just fine.

The model I am trying to work with looks something like this:

(Note that I have renamed each property and removed datatypes that were used more than once)

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; }

    }

I configured the sequences inside the modelbuilder like this:

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

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

I'm trying to call MyDbContext.Add() like this:

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

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

I'm not sure if there was a breaking change from EF Core 2.2 to 3.1 involving sequences or if I've set up something incorrectly when making my new project and moving models over manually.

Please let me know if you need any further information.

Edit: The full error message with a stack trace is this:

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)

Apologies in advance if the stack trace is too long. I didn't know exactly what information you would need.

1 Answers

The issue is with your TimeStamp property. Try marking it with NotMappedAttribute and the error will go away. I can't explain what internal EF magic makes it behave this way, but personally I highly recommend against using "non-trivial" properties in EF models, like your TimeStamp one.

UPD

It seems that EF considers private DateTime? timeStamp; to be backing field for TimeStamp property, despite documentation saying that backing filed should start with _ or m_, but this answer states that camelcase properties also work, and renaming timeStamp to something like timeStampss makes error go away too.

Also see this breaking change for backing fields in 3.0.

Related