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

时间:2017-07-25 23:37:57

标签: c# sql-server entity-framework-core

我有两个项目

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

1 个答案:

答案 0 :(得分:1)

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

  

在分类中讨论并决定我们应该有ModelBuilder工厂,以便人们不必担心约定等(特别是因为约定是第一版的内部&#39;)

     

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

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();

修正了我。