DbContext尝试将非空字段保存为null

时间:2021-10-20 02:32:43

I have two projects

我有两个项目

StoreSku.Data - Depends On: Microsoft.EntityFrameworkCore
StoreSku.Provider - Depends On: StoreSku.Data, Microsoft.EntityFrameworkCore.SqlServer

I don't want to tie StoreSku.Data to any particular provider, so I've moved that reposonsibility to StoreSku.Provider. I'm creating the model and options within StoreSku.Provider and passing the options into the DbContext using the following code:

我不想将StoreSku.Data绑定到任何特定的提供程序,因此我将该可重用性转移到StoreSku.Provider。我正在StoreSku.Provider中创建模型和选项,并使用以下代码将选项传递到DbContext:

var optionsBuilder = new DbContextOptionsBuilder();
optionsBuilder.UseSqlServer(connectionString);

var conventions = new ConventionSet();
var modelBuilder = new ModelBuilder(conventions);

modelBuilder.Entity<StoreSkuUpdateRequest>().HasKey(request => request.Id);
modelBuilder.Entity<StoreSkuUpdateRequest>().Property(request => request.Id).UseSqlServerIdentityColumn();
modelBuilder.Entity<StoreSkuUpdateRequest>().Property(request => request.StoreNumber).ValueGeneratedNever();
modelBuilder.Entity<StoreSkuUpdateRequest>().Property(request => request.BusinessDate).ValueGeneratedNever();
modelBuilder.Entity<StoreSkuUpdateRequest>().Property(request => request.Timestamp).ValueGeneratedNever();
modelBuilder.Entity<StoreSkuUpdateRequest>().HasMany(request => request.RequestedUpdates).WithOne(update => update.UpdateRequest);
modelBuilder.Entity<StoreSkuUpdateRequest>().HasMany(request => request.RelatedUpdates).WithOne(relatedUpdate => relatedUpdate.UpdateRequest);
modelBuilder.Entity<StoreSkuUpdateRequest>().ForSqlServerToTable("StoreSkuUpdateRequest");

modelBuilder.Entity<StoreSkuAvailabilityUpdate>().HasOne(update => update.UpdateRequest).WithMany(request => request.RequestedUpdates);
modelBuilder.Entity<StoreSkuAvailabilityUpdate>().HasMany(update => update.RelatedUpdates).WithOne(relatedUpdate => relatedUpdate.RequestedUpdate);
modelBuilder.Entity<StoreSkuAvailabilityUpdate>().ForSqlServerToTable("RequestedStoreSkuUpdate");

modelBuilder.Entity<RelatedStoreSkuUpdate>().HasOne(relatedUpdate => relatedUpdate.RequestedUpdate).WithMany(update => update.RelatedUpdates);
modelBuilder.Entity<RelatedStoreSkuUpdate>().HasOne(relatedUpdate => relatedUpdate.UpdateRequest).WithMany(request => request.RelatedUpdates);
modelBuilder.Entity<RelatedStoreSkuUpdate>().ForSqlServerToTable("RelatedStoreSkuUpdate");

optionsBuilder.UseModel(modelBuilder.Model);

DbContextOptions options = optionsBuilder.Options;

As you can see, it's pretty verbose. Whether I need all of these calls is a different question. My question focuses more along these lines:

正如你所看到的,它非常冗长。我是否需要所有这些电话是一个不同的问题。我的问题更侧重于以下几点:

modelBuilder.Entity<StoreSkuUpdateRequest>().Property(request => request.StoreNumber).ValueGeneratedNever();
modelBuilder.Entity<StoreSkuUpdateRequest>().Property(request => request.BusinessDate).ValueGeneratedNever();
modelBuilder.Entity<StoreSkuUpdateRequest>().Property(request => request.Timestamp).ValueGeneratedNever();

The error that I've run into is that if I don't have these lines, I get the following errors:

我遇到的错误是,如果我没有这些行,我会收到以下错误:

Cannot insert the value NULL into column 'StoreNumber', table 'dbo.StoreSkuUpdateRequest'; column does not allow nulls. INSERT fails.
The statement has been terminated.
Cannot insert the value NULL into column 'BusinessDate', table 'dbo.StoreSkuUpdateRequest'; column does not allow nulls. INSERT fails.
The statement has been terminated.
Cannot insert the value NULL into column 'Timestamp', table 'dbo.StoreSkuUpdateRequest'; column does not allow nulls. INSERT fails.
The statement has been terminated.

The schema for this table is

该表的架构是

CREATE TABLE [dbo].[StoreSkuUpdateRequest](
[Id] [bigint] IDENTITY(1,1) NOT NULL,
[StoreNumber] [int] NOT NULL,
[BusinessDate] [date] NOT NULL,
[Timestamp] [datetime] NOT NULL)

And the model

和模型

public sealed class StoreSkuUpdateRequest
{
    [Required]
    public long Id { get; set; }

    [Required]
    public int StoreNumber { get; set; }

    [Required]
    public DateTime BusinessDate { get; set; }

    [Required]
    public DateTime Timestamp { get; set; }
}

And the calling code:

和调用代码:

using (Context)
{
    var request = new StoreSkuUpdateRequest
    {
        StoreNumber = 12345,
        BusinessDate = DateTime.UtcNow,
        Timestamp = DateTime.UtcNow
    };
    Context.StoreSkuAvailabilityUpdateRequests.Add(request);

    Context.SaveChanges();
}

And the SQL command being generated is

并且生成的SQL命令是

BEGIN TRANSACTION

SET NOCOUNT ON;
INSERT INTO [StoreSkuUpdateRequest]
DEFAULT VALUES;
SELECT [Id]
FROM [StoreSkuUpdateRequest]
WHERE @@ROWCOUNT = 1 AND [Id] = scope_identity();

ROLLBACK TRANSACTION 

Why do I have to specify ValueGeneratedNever(); for each of these properties? Why is EFCore trying to save them as null?

为什么我必须指定ValueGeneratedNever();对于这些属性中的每一个?为什么EFCore试图将它们保存为null?

1 个解决方案

#1


1  

Based on the comment from Ivan Stoev in the original post, I stumbled upon this little bit from https://github.com/aspnet/EntityFramework/issues/3529

基于Ivan Stoev在原帖中的评论,我偶然发现了这一点来自https://github.com/aspnet/EntityFramework/issues/3529

Discussed in triage and decided we should have ModelBuilder factories so that folks don't need to worry about conventions etc. (especially since conventions are 'internal' for the first release).

在分类中讨论并决定我们应该有ModelBuilder工厂,以便人们不需要担心约定等(特别是因为约定是第一个版本的'内部')。

Folks can just create one up like this (note this is a static Create() method on the factory rather than a default ctor to get a factory since having a default ctor doesn't play well with DI):

人们可以像这样创建一个(注意这是工厂的静态Create()方法而不是默认的ctor来获得工厂,因为默认的ctor不能很好地与DI一起使用):

var builder = SqlServerModelBuilderFactory.Create()

var builder = SqlServerModelBuilderFactory.Create()

Alternatively, if you want it to get all it's services from DI then we'll register one that you can just resolve... then you will get the same logger, etc. that is registered in DI (plus if you have overridden the type mapper etc. then you will also get those services).

或者,如果您希望它从DI获得所有服务,那么我们将注册一个您可以解决的服务...然后您将获得在DI中注册的相同记录器等(如果您已覆盖该类型,则另外mapper等然后你也会得到那些服务)。

sp.AddEntityFramework() .AddSqlCompact() .AddNpgSql() .AddSqlServer();

sp.AddEntityFramework()。AddSqlCompact()。AddNpgSql()。AddSqlServer();

var builder = sp.GetService<ISqlServerModelBuilderFactory>().Create();

var builder = sp.GetService ()。Create();

I wasn't able to find a SqlServerModelBuilderFactory class, which led me to investigate if there was a SqlServerConventionSetBuilder; there was.

我无法找到SqlServerModelBuilderFactory类,这导致我调查是否有SqlServerConventionSetBuilder;有。

So changing it from

所以改变它

var conventions = new ConventionSet();

to

var conventions = SqlServerConventionSetBuilder.Build();

Fixed me right up.

我修好了。

#1


1  

Based on the comment from Ivan Stoev in the original post, I stumbled upon this little bit from https://github.com/aspnet/EntityFramework/issues/3529

基于Ivan Stoev在原帖中的评论,我偶然发现了这一点来自https://github.com/aspnet/EntityFramework/issues/3529

Discussed in triage and decided we should have ModelBuilder factories so that folks don't need to worry about conventions etc. (especially since conventions are 'internal' for the first release).

在分类中讨论并决定我们应该有ModelBuilder工厂,以便人们不需要担心约定等(特别是因为约定是第一个版本的'内部')。

Folks can just create one up like this (note this is a static Create() method on the factory rather than a default ctor to get a factory since having a default ctor doesn't play well with DI):

人们可以像这样创建一个(注意这是工厂的静态Create()方法而不是默认的ctor来获得工厂,因为默认的ctor不能很好地与DI一起使用):

var builder = SqlServerModelBuilderFactory.Create()

var builder = SqlServerModelBuilderFactory.Create()

Alternatively, if you want it to get all it's services from DI then we'll register one that you can just resolve... then you will get the same logger, etc. that is registered in DI (plus if you have overridden the type mapper etc. then you will also get those services).

或者,如果您希望它从DI获得所有服务,那么我们将注册一个您可以解决的服务...然后您将获得在DI中注册的相同记录器等(如果您已覆盖该类型,则另外mapper等然后你也会得到那些服务)。

sp.AddEntityFramework() .AddSqlCompact() .AddNpgSql() .AddSqlServer();

sp.AddEntityFramework()。AddSqlCompact()。AddNpgSql()。AddSqlServer();

var builder = sp.GetService<ISqlServerModelBuilderFactory>().Create();

var builder = sp.GetService ()。Create();

I wasn't able to find a SqlServerModelBuilderFactory class, which led me to investigate if there was a SqlServerConventionSetBuilder; there was.

我无法找到SqlServerModelBuilderFactory类,这导致我调查是否有SqlServerConventionSetBuilder;有。

So changing it from

所以改变它

var conventions = new ConventionSet();

to

var conventions = SqlServerConventionSetBuilder.Build();

Fixed me right up.

我修好了。