DbContext试图将非空字段保存为null。

4

我有两个项目

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

我不想将 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;

你可以看到,这相当冗长。我是否需要所有这些调用是另一个问题。我的问题更集中在以下方面:

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

我遇到的错误是,如果我没有这些行,我会得到以下错误:
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.

这个表的架构是

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

同时这个模型

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

接下来是调用代码:

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

    Context.SaveChanges();
}

生成的 SQL 命令是:

BEGIN TRANSACTION

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

ROLLBACK TRANSACTION 

为什么我需要为每个属性指定ValueGeneratedNever();?为什么EFCore尝试将它们保存为null?

StoreSkuUpdateRequest和StoreSkuAvailabilityUpdate之间的关系是什么?我有点困惑,因为您正在创建一个UpdateRequest,但随后将其添加到AvailabilityUpdateRequests中?两者之间似乎存在(历史)关系,但如果这归结为EF必须解决的FK关系,则可能会执行类似于Insert+Update的情况。使用SQL分析器检查SaveChanges时发送到数据库的确切内容。 - Steve Py
2
你的问题核心原因是使用了一个空的ConventionSet创建了ModelBuilder,而正常的OnModelCreating方法中传递的ModelBuilder则是使用包含所有EF Core约定的ConventionSet进行初始化的。 - Ivan Stoev
@IvanStoev 我也曾这么想,但我找不到任何形式的 AllConventionsSet,而且所有实现都被记录为 此 API 支持 Entity Framework Core 基础结构,不应直接从您的代码中使用。此 API 可能会在未来的版本中更改或删除。 - Zymus
1个回答

1

根据原始帖子中Ivan Stoev的评论,我发现了这个来自https://github.com/aspnet/EntityFramework/issues/3529的小片段。

经过讨论,我们决定应该有ModelBuilder工厂,这样人们就不用担心约定等问题了(特别是因为约定在第一个版本中是“内部的”)。

人们可以像这样创建一个(请注意,这是工厂上的静态Create()方法,而不是默认构造函数,以获取工厂,因为默认构造函数与DI不兼容):

var builder = SqlServerModelBuilderFactory.Create()

或者,如果您希望它从DI中获取所有服务,则我们将注册一个,您只需解析即可...然后您将获得在DI中注册的相同记录器等内容(此外,如果您已重写类型映射器等,则还将获得这些服务)。

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

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

我无法找到一个名为 SqlServerModelBuilderFactory 的类,这促使我调查是否有一个 SqlServerConventionSetBuilder;结果确实存在。
因此,将它从原来的形式更改为:
var conventions = new ConventionSet();

var conventions = SqlServerConventionSetBuilder.Build();

修好了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接