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

我有两个项目

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?

最佳答案 根据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).

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):

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).

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

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

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

所以改变它

var conventions = new ConventionSet();

var conventions = SqlServerConventionSetBuilder.Build();

修正了我.

点赞