首页 > 编程语言 >[学习笔记]解决因C#8.0的语言特性导致EFCore实体类型映射的错误

[学习笔记]解决因C#8.0的语言特性导致EFCore实体类型映射的错误

时间:2023-05-18 19:12:15浏览次数:41  
标签:8.0 set string get C# 引用 EFCore 类型 public

今天下午在排查一个EF问题时,遇到了个很隐蔽的坑,特此记录。

问题

使用ef执行Insert对象到某表时报错,此对象的Address为空:

 不能将值 NULL 插入列 'Address',表 'dbo.xxx';列不允许有 Null 值。INSERT 失败。

检查数据库和迁移文件时发现Address这个字段被意外设置成nullable: false,而其它的字段却正常,按理来说对于string类型的属性,EFCore在codefirst模式下应该映射为可空类型。

代码也确认了实体中不包含[Required]注释,在任何地方也没有出现.IsRequired()的调用。

于是开始排查:手动创建一个空程序集,引用EFCore,从原项目拷贝EF设计时库、DbContext和各实体类,一顿操作后竟然发现在新的程序集中生成的迁移文件是符合预期的。
令人费解,在多次比对代码之后,发现是.csproj文件中的这一行配置导致的

<Nullable>enable</Nullable>

原因分析

C# 8 引入了一项名为可为 null 引用类型 (NRT) 的新功能。官方文档
该功能允许对引用类型进行批注,指示引用类型能否包含 null。

通过查看EF文档了解到,可为空引用类型通过以下方式影响 EF Core 的行为:

  • 如果禁用可为空引用类型,则按约定将具有 .NET 引用类型的所有属性配置为可选 (例如 string ) 。
  • 如果启用了可为 null 的引用类型,则基于属性的 .NET 类型的 C# 为 Null 性来配置属性:string? 将配置为可选属性,但 string 将配置为必需属性。

换而言之,启用了该功能后,把原本《引用类型可为空》的这个传统约定,更改称为了《引用类型是否可为空,是通过?语法来表明的》,实体中string类型的属性在C#中作为引用类型,自然而然地受到了这个影响。

果然,在删除了这个功能后,string?的语法将不起作用

在这里插入图片描述

解决

关闭此功能,重新生成迁移,更新数据库,问题解决。

后记

语言特性会影响EF实体与表结构映射的约定,官方示例中对于string类型的处理方式也做了说明:

无NRT


public class CustomerWithoutNullableReferenceTypes
{
    public int Id { get; set; }

    [Required] // Data annotations needed to configure as required
    public string FirstName { get; set; }

    [Required]
    public string LastName { get; set; } // Data annotations needed to configure as required

    public string MiddleName { get; set; } // Optional by convention
}

有NRT

public class Customer
{
    public int Id { get; set; }
    public string FirstName { get; set; } // Required by convention
    public string LastName { get; set; } // Required by convention
    public string? MiddleName { get; set; } // Optional by convention

    // Note the following use of constructor binding, which avoids compiled warnings
    // for uninitialized non-nullable properties.
    public Customer(string firstName, string lastName, string? middleName = null)
    {
        FirstName = firstName;
        LastName = lastName;
        MiddleName = middleName;
    }
}

这两种模型的数据库映射是等价的。

之后应留意项目的"NRT"功能是否开启,在解决方案.csproj文件中用如下方式关闭

<Nullable>disable</Nullable>

留意实体类中是否有代码段被标识"NRT"功能开启

#nullable disable

#nullable enable

从 .NET 6 开始,默认情况下会为新项目启用这些功能。原始项目是.NET 5.0升级而来的,所以项目文件中并不会包含Nullable相关的配置。

为了一行bug,好值得的一个下午呢

标签:8.0,set,string,get,C#,引用,EFCore,类型,public
From: https://www.cnblogs.com/jevonsflash/p/17413053.html

相关文章

  • Codeforces Round 873 (Div. 2)
    Preface补题,这场本来周日晚上打算现场打的但一来第二天要上课怕熬太晚影响早上的微积分和电分,二来那天晚上开了DP专题然后就在手速抢一血过程中被两道DFS搞红温了,本来打CF的计划也咕咕咕了今天差不多想起来好久没做CF了赶紧补一场看了下自己补题的时候2h也才堪堪做完D1,虽然后......
  • Codeforces Round 703 (Div. 2) A-D
    CodeforcesRound703(Div.2) A.ShiftingStacksinta[N];voidsolve(){intn=read(),ans=1;for(inti=1;i<=n;i++)a[i]=read();intrest=0,last=-1;for(inti=1;i<=n;i++){a[i]+=rest;rest=a[i]-last-1;last++......
  • [每天例题]蓝桥杯 C语言 次数差
    次数差题目  思路分析1.通过字符型数组接收字符串,通过整形数组确定字母出现的次数2.通过for—if寻找出现次数最多与最少的字母,注意,这里有个坑,出现次数最少的字母至少出现一次代码#include<stdio.h>intmain(){ chars[1000]; intnum[26]={0}; intmax=-1,min=10......
  • C++
    文件定义一个Dog类,包括体重和年龄两个数据成员及其成员函数,声明一个实例dog1,体重5,年龄10,使用I/O流把dog1的状态写入磁盘文件。再声明一个实例dog2,通过读取文件dog1的状态赋给dog2。#include<iostream>#include<fstream>usingnamespacestd;classDog{public:      ......
  • OpenCloudOS 如何基于 eBPF 实现容器级别的TCP 连接监控?
    eBPF技术的出现,使得内核的资源监控更加的便捷、高效,容器化监控也更加适用于云原生的场景。基于eBPF实现的可观测性,可以无需修改内核源码或者加载内核模块,安全高效的扩展内核功能。本文,将从网络的角度介绍如何基于eBPF,实现容器级别的TCP连接监控。一、技术背景OpenCloudO......
  • CleanMyWechat是一个用于自动删除PC端微信缓存数据的工具
    CleanMyWechat是一个用于自动删除PC端微信缓存数据的工具。它可以删除微信自动下载的大量文件、视频和图片等数据,从而释放存储空间。工具支持识别微信账号,允许用户选择自定义路径,并且可以管理多个账号。删除后的文件会被放置在回收站中,以防止意外删除。该工具支持Windows系统中的......
  • 2020 AHUCPC
    A题目描述给出两个整数A和B,可以重新排列A得到新的数字C(不能有前导0)。求在小于等于B的情况下,C的最大值是多少。如果不存在输出-1。#include<bits/stdc++.h>usingnamespacestd;intmain(){strings;intb;cin>>s>>b;intk=-999;sort(s.begin()......
  • echart常用的几个api函数
    在对echart进行二次封装时,以下几个api函数很有用。首先是,init和dispose,我们在创建页面及页面卸载时可以使用,让echart的资源能在组件卸载时被释放。this.chart=echarts.init(this.$refs.echart);this.$once('hook:beforeDestroy',()=>{this.chart.dispose();})......
  • wpf XAML 设计器异常,提示NullReferenceException 未将对象引用设置到对象
     在cs构造函数里手动注册,并且在控件的构造函数里增加判断if(DesignerProperties.GetIsInDesignMode(this)){return;}//在这里才注册Load事件cmbSpeed.Loaded+=cmbSpeed_Loaded;来源:https://www.cnblogs.com/zsx-blog/p/8311633.html ......
  • Docker安装Airflow
    环境系统:Ubuntu22.04.2LTSdockerVersion:20.10.21docker-composeversion1.29.2,python3--versionPython3.10.61、安装docker1.1先更新系统 sudoapt-getupdate sudoapt-getupgrade1.2安装docker: apt-getinstalldocker.io1.3查看docker版本: ......