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

今天下午在排查一个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,好值得的一个下午呢

本文来自博客园,作者:林晓lx,转载请注明原文链接:http://www.cnblogs.com/jevonsflash/p/17413053.html

本文转载于网络 如有侵权请联系删除

相关文章

  • 详解一次由读写锁引起的内存泄漏

    JVM相关的异常,一直是一线研发比较头疼的问题。因为对于业务代码,JVM的运行基本算是黑盒,当异常发生时,较难直观的看到和找到问题所在,这也是我们一直要研究其内部逻辑的原因。本篇就由一个近期线上JVM内存泄漏的例子,带大家强行分析一波~Part1线上服务器报警了某天,同事来找我帮忙,原来是某系统毫无征兆的来了一连串报警,一波机器的老年代内存占用率超过阈值~1.1先看表现老年代内存占用可以看到,在7月中旬之前,内存占用还是比较正常的,每次GC都可以回收掉很大一部分的老年代对象。而中旬之后,老年代内存一直缓慢增长而无法释放。很明显,应该是对象没法被正常回收导致。内存泄漏了~1.2怎么办呢如果是刚上线的项目爆出了此类问题,因为影响面比较小,可以直接先回滚代码,止血为第一要务。不过,这个项目明显已经上线N多天,中间还不知道上过多少需求,而且,既然流量近期有上涨导致问题出现,说明,已经对客开流量了。回滚是不可能了,抓紧时间定位问题,上线修复吧。Part2定位问题一般的步骤:拿到dump文件用MAT等工具,找出内存占用过多的异常对象,以及引用关系分析异常对象关联代码的可能问题不过,因为这次dump

  • 腾讯云支付系统架构介绍

    近年来,得益于丰富的场景、便捷的服务,移动支付用户总量和支付频度持续快速增加,移动支付已经成为人们的生活习惯。但是便捷之后也暗藏隐忧,调查报告显示(《2017移动支付用户调研报告》),商户不支持和安全隐患成为移动支付用户最担心的问题,其次是付费失败等问题。腾讯云支付是借力TEG多年沉淀的技术能力,由腾讯云联合微信支付推出的移动收单SaaS服务,旨在为商户提供一个安全、稳定、高效、易用、低成本接入微信支付的解决方案,助力移动支付行业快速健康地发展。——引言一、云支付是什么1.1项目背景微信支付面临的问题:ISV的质量参差不齐:为商户开发接入微信支付系统的ISV质量参差不齐,存在系统的稳定性、安全性不高,普通用户使用微信支付进行支付时体验差的问题,降低用户对微信支付的信心。普通服务商面临的问题:技术门槛高:大部分服务商没有能力开发对接微信支付的收单系统。系统成本高:市场上少量高质量的系统,价格昂贵,服务商难以承担。1.2项目定位云支付旨在提供端到端(从用户到微信支付以及其他第三方支付渠道)的安全、稳定、高效、易用、低成本的商业支付解决方案,完善从用户到支付渠道的最后一公里。对于微信支付:提

  • 一文搞懂matplotlib中的颜色设置

    在matplotlib中,颜色设置有以下多种方式1.常用颜色的字母表示及缩写最常用的颜色表示方法,有以下几种常用颜色1.red,表示红色,简写为r2.green,表示绿色,简写为g3.blue,表示蓝色,简写为b4.yellow,表示黄色,简写为y5.cyan,表示蓝绿色,简写为c6.magenta,表示粉紫色,简写为m7.black,表示黑色,简写为k8.white,表示白色,简写为w上述颜色和缩写的图例如下2.T10调色盘在matplotlib中,默认的颜色盘通过参数rcParams["axes.prop_cycle"]参数来指定,初始的调色盘就是T10调色盘。T10调色盘适用于离散分类,其颜色名称以tab:为前缀,具体的包含了以下10种颜色1.tab:blue2.tab:orange3.tab:green4.tab:red5.tab:purple6.tab:brown7.tab:pink8.tab:gray9.tab:olive10.tab:cyan图例如下在matplotlib中,默认就是通过这个T10调色盘来个不同的label上色的,代码如下plt.pie

  • 京东合作周志华:京东AI研究院南京分院启动

    【导读】近期有消息透露,京东最新与人工智能领域内大牛——周志华教授达成合作,周志华教授将出任京东集团人工智能研究院学术委员会委员,同时京东集团已启动在南京建立京东人工智能研究院南京分院,届时周志华教授将担任该分院学术总顾问。周志华教授:周志华教授是南京大学计算机科学与技术系(学士、硕士、博士)的本土人才,现任南京大学计算机科学与技术系副主任、软件新技术国家重点实验室常务副主任,机器学习与数据挖掘研究所(LAMDA)所长,校、系学术委员会委员,同时也是南京大学新成立的人工智能学院院长(兼)。周志华教授是AAAIFellow,IEEEFellow,IAPRFellow,并于2016年入选ACMFellow和AAASFellow,成为国际上与人工智能相关的重要学会“大满贯”Fellow华人第一人。学习过人工智能和机器学习相关知识的同学应该都知道,周志华教授最为人所熟知的莫过于其经典西瓜书《机器学习》。他的主要研究方向是人工智能、机器学习、数据挖掘等领域的研究工作。京东人才引进:近年来,京东在人工智能方向也是颇下功夫,在人才引进方面也是不遗余力,在过去几个月,京东通过“挖人”战略已经聚集了大量

  • 绘制SVG内容到Canvas的HTML5应用

    SVG与Canvas是HTML5上绘制图形应用的两种完全不同模式的技术,两种绘制图形方式各有优缺点,但两者并非水火不容,尤其是SVG内容可直接绘制在Canvas上的功能,使得两者可以完美的融合在一起,让Canvas可享用到现有丰富的SVG素材,并不失SVG矢量无级缩放的特点。《基于HTML5的DragandDrop生成图片Base64信息》这篇虽然展示的是拖拽普通栅格图片的效果,但你也可以直接拖拽SVG格式的图片进行显示,只不过普通图片的格式数据为data:image/png类型,而SVG格式的数据类型为data:image/svg+xml的类型,下图为该HTforWeb拓扑图拖拽入SVG格式图片的运行效果:以下一段小例子,展示了加载一个SVG图片后,分为七个基本进行缩放绘制的效果,可看出Canvas绘制SVG可保持其矢量不失真的特性functiondraw(){ varimg=newImage(); img.src='chart.svg'; document.body.appendChild(img); img.onload=function(){ varcanv

  • 快速入门Python机器学习(35)

    14.2数据表达与特征工程14.2.1数据表达哑变量:利用类似pd.get_dummies得到的0,1数据。importnumpyasnp importpandasaspd importmatplotlib.pyplotasplt fromsklearn.neural_networkimportMLPRegressor fromsklearn.neighborsimportKNeighborsRegressor fromsklearn.preprocessingimportOneHotEncoder#独热编码,同pd.get_dummies,但仅能用于整型变量 defbase_for_dummies(): colour=pd.DataFrame({'数据特征':[1,2,3,4,5,6,7],'颜色类型':['赤','橙','黄','绿','青','蓝','紫']}) print(colour)复制输出数据特

  • zblog企业展示型主题模板赢天下(Winlee)助力小微企业成长

    嗨,我又来了,话说可以简单聊聊为什么会出这款主题,端午节假日在家,孩子午睡时间自己更新完主题之后网上冲浪,偶然间看到很多工作室和小微企业的网站,有些真的惨目认读,杂乱无章,瞬间灵感迸发,赢天下主题模板也就应运而生(怎么感觉跟写小说是的呢),说人话就是弄了一款小微企业主题,助力企业成长,以最少的资金获得最好的服务,毕竟现在的企业网站建设都是3-5K起步,万八千的也不少,更何况还有几千万搭建商城结果还运行不了的,悲伤的表情.gif。本站也出过几款主题模板,感觉文章l列表信息多,不像是单纯的企业展示类模板,所以重组了之前的思路,按照小微企业的现状制作出了这款小微企业主题模板,希望您给您的企业带来一丝惊喜!好了,简单介绍下,赢天下主题模板首页和分类页是没有侧栏的,文章页有侧栏但是有开关,自行决定是否需要,默认开启。主题重写SEO规范,兼容其他SEO插件。主题自带三个广告位,分别是:分类顶部,文章页和文章推荐,按需开启!        -可使用HTML代码或者联盟广告代码,html代码参考:Z-blogPHP常见问题答疑,里面有详细广告代码。新增视频展示        -就目前而言越来越多的网站

  • 腾讯云智能媒资托管公共参数调用方式

    公共参数是用于标识用户和接口签名的参数,如非必要,在每个接口单独的接口文档中不再对这些参数进行说明,但每次请求均需要携带这些参数,才能正常发起请求。 公共参数的具体内容会因您使用的签名方法版本不同而有所差异。 使用签名方法v3的公共参数签名方法v3(有时也称作TC3-HMAC-SHA256)相比签名方法v1(有些文档可能会简称签名方法),更安全,支持更大的请求包,支持POSTJSON格式,性能有一定提升,推荐使用该签名方法计算签名。完整介绍详见签名方法v3。 注意:接口文档中的示例由于目的是展示接口参数用法,简化起见,使用的是签名方法v1GET请求,如果依旧想使用签名方法v1请参考下文章节。 使用签名方法v3时,公共参数需要统一放到HTTPHeader请求头部中,如下表所示: 参数名称 类型 必选 描述 Action String 是 HTTP请求头:X-TC-Action。操作的接口名称。取值参考接口文档中输入参数公共参数Action的说明。例如云服务器的查询实例列表接口,取值为DescribeInstances。 Region String - HTTP请求头:X-

  • 腾讯云负载均衡技术原理

    负载均衡CLB提供四层(TCP协议/UDP协议/TCPSSL协议)和七层(HTTP协议/HTTPS协议)负载均衡。您可以通过CLB将业务流量分发到多个后端服务器上,消除单点故障并保障业务可用性。CLB自身采用集群部署,可实现会话同步,消除服务器单点,提升系统冗余,保证服务稳定,可在同一个地域部署多个机房,实现同城容灾。 基础架构腾讯云负载均衡当前提供四层和七层的负载均衡服务: 四层主要基于腾讯自研的统一接入网关(TencentGateway,TGW)来实现负载均衡,TGW具有可靠性高、扩展性强、性能高、抗攻击能力强等特点,支持DataPlaneDevelopmentKit(DPDK)高性能转发,单集群可支持亿级并发、千万级PPS。腾讯内部诸多业务均通过TGW接入服务,包括腾讯游戏、腾讯视频、微信、QQ等。 七层主要基于SecureTencentGateway(STGW)实现负载均衡,STGW是腾讯基于Nginx自研的支持大规模并发的七层负载均衡服务,承载了腾讯内大量的七层业务流量,包括腾讯新闻、理财通、腾讯游戏、微信等。 转发路径负载均衡负责转发业务流量,由后端服务实际处理业务请求

  • 【前缀和】和为K、和可被K整除的子数组

      连续子数组问题是算法中经常可以见到的一类题目,通过几个典型的题目分析,可以发现这类题目主要分为两大类,其解题思路通过最简单的子串枚举(枚举所有的子串起点和终点)来暴力解决大都不难,但是如果考虑到对空间和时间的要求,其解答就需要一定的算法技巧。 子数组和问题(前缀和+哈希表) 子数组最值问题(多阶段决策过程最优化问题,动态规划) 【子数组】子数组和问题(前缀和)   一看到子数组和,有必要先对前缀和思想进行一些考虑。前缀和指的是:数组第0项到当前项的总和,类似于我们在数学中学到的数列的前n项和。   如果用一个数组pre[]表示:pre[i]=nums[0]+nums[1]+···+nums[i]   前缀和的优势在于:数组中的某一项可以表示为相邻前缀和之差:nums[i]=pre[i]-pre[i-1],因此,从i到j范围子数组和就可以表示为:nums[i]+nums[i+1]+···+nums[j]=pre[j]-pre[i-1],这一关系就为求解子数组和问题提供了数学依据。 560、和为K的子数组   题目描述:给定一个整数数组和一个整数k,你需要找到该数组中和为k的连续

  • Actor模型研究二

    1介绍1.1什么是actor对于刚接触actor的我,第一感觉就像redis一样,每个actor就是一个redis实例,都有自己消息队列,actor相互通信通过将消息发给对方,消息发送进对方的消息队列,等待对方线程处理。来看看我们之前做项目的痛点。 游戏服务器通常分为多个服,每个服上有多个玩家。假设玩家与玩家数据交互操作,这时怎么避免数据竞争?两种解决方案: 1、将数据写入redis 首先redis效率高,也是单线程模型,不存在数据竞争,但是也有它的不足之处,操作redis会受网络影响,即使再快也避免不了网络io的开销。goredissdk在序列化响应回来的消息时,会序列化成字符串,大量的操作会造成gc的压力。 2、加锁 这个就是很常用的做法,避免数据竞争,但是玩家其实大部分数据都是自身有的,属于玩家自己,如果每个玩家操作别人数据都加锁(例如扣血),在大型游戏如果王者荣耀这种实时性高的游戏,性能估计会很差。但是在面对mongo事务写冲突时,加互斥锁估计就无法解决了。这时候只能将粒度调大,调大在事务级别,这样性能就更差了。一旦业务处理耗时太长,所有相关玩家都将会感觉到卡顿操作。 有没有更

  • Git常用命令使用大全

    查看、添加、提交、删除、找回,重置修改文件 githelp<command>#显示command的help gitshow#显示某次提交的内容gitshow$id gitco--<file>#抛弃工作区修改 gitco.#抛弃工作区修改 gitadd<file>#将工作文件修改提交到本地暂存区 gitadd.#将所有修改过的工作文件提交暂存区 gitrm<file>#从版本库中删除文件 gitrm<file>--cached#从版本库中删除文件,但不删除文件 gitreset<file>#从暂存区恢复到工作文件 gitreset--.#从暂存区恢复到工作文件 gitreset--hard#恢复最近一次提交过的状态,即放弃上次提交后的所有本次修改 gitci<file>gitci.gitci-a#将gitadd,gitrm和gitci等操作都合并在一起做                                    gitci-am"somecomments" gitci--amend#修改最后一

  • Android模拟器对应的电脑快捷键说明

    Android模拟器对应的电脑快捷键说明,需要的朋友可以参考一下 Home键(小房子键) 在键盘上映射的就是home键,这倒是很好记。 Menu键 用于打开菜单的按键,在键盘上映射的是F2键,PgUp键同样可以。另外,看英文原文的意思,貌似这个键在某些机型上会被设计为左软件(leftsoftkey) Start键 这个键在模拟器和G1真机上我都没有找到到底是哪个键。映射的是Shift+F2或PgDn,某些机型会被设计为右软键(rightsoftkey)。 Back键 返回键,用户返回上一个UI或者退出当前程序。键盘上映射ESC键。 Call/Dial键(电话键) 接听来电或启动拨号面板,这是一部手机最基本的功能键。PC键盘映射为F3键。 Hangup/LightOff键(挂机键) 挂断电话或关闭背灯用。键盘映射F4键。 Search键 在提供了Search功能的应用里快速打开Search对话框,比如在Browser里可以快速打开地址搜索栏。键盘映射F5。 PowerDown键(关闭电源) 对应模拟器左上边缘的电源按钮,不过似乎在模拟器上按这个键并没什么用处。键盘映射F7。 Volume

  • Reverse Nodes in k-Group

    描述: Givenalinkedlist,reversethenodesofalinkedlistkatatimeandreturnitsmodifiedlist. Ifthenumberofnodesisnotamultipleofkthenleft-outnodesintheendshouldremainasitis. Youmaynotalterthevaluesinthenodes,onlynodesitselfmaybechanged. Onlyconstantmemoryisallowed. Forexample,Giventhislinkedlist:1->2->3->4->5 Fork=2,youshouldreturn:2->1->4->3->5 Fork=3,youshouldreturn:3->2->1->4->5  代码: classSolution{ public:   ListNode*reverseKGroup(ListNode*he

  • 第三次

    需求分析: 1.编写一个能对0~10之间的整数进行四则运算的程序。 2.程序能接收用户输入的整数结果,并判断对错,在程序结束时,统计出答对和答错的题目数量。 3.0~10整数是随机出现。 4.增加内容: (1)处理用户的错误输入,除法运算中分母不为0,减法结果不为负数。 (2)用户自己设定倒计时。 (3)用户自己可以设定随机整数的范围和答题的数量。 (4)用户自己可以自己选择哪种计算类型,也可以让软件随机生成四则运算中的一种。 思路:1.还是选择窗体应用程序,在上一次的作业的基础上,进行完善。 2.这次是结对编程,需要两人合作完成,我负责查看错误,队友负责写代码。 3.需要考虑添加倒计时控件,以及编写代码。定义随机整数的范围和答题的数量,选择计算类型,处理用户的错误输入,除法运算中分母不为0,减法结果不为负数。 代码: usingSystem; usingSystem.Collections.Generic; usingSystem.ComponentModel;usingSystem.Data; usingSystem.Drawing;usingSystem.Linq;usingSy

  • 简单粗暴的移动端页面开发技能

    移动端响应式页面开发说简单也简单,根据屏幕尺寸调节根字体大小。 大宽度用%,高度和小宽度全部使用rem,简单粗暴。 之前阅读过大漠老师的使用Flexible实现手淘H5页面的终端适配,介绍了手淘项目的H5制作规范。 在手淘的设计师和前端开发协作过程中:手淘设计师常选择iPhone6作为基准设计尺寸,交付给前端的设计尺寸是按750px*1334px为准(高度会随着内容多少而改变)。前端开发人员通过一套适配规则自动适配到其他的尺寸。 而我这次负责的项目为公司刚上线的手游内置网页,展示游戏公告和游戏角色信息。 因为手游是横屏的,所以设计稿也有了些改变。内容尽可能集中在横向,减少用户上下滑动的次数。所以设计师按照960px出的稿。 前端步骤 设置根字体大小 html{font-size:100px;} 复制 js调节1rem对应的px大小 $(document).ready(function(){ //putallyourjQuerygoodnessinhere. functionadjust(){ //设计稿宽度是960px varscale=$('body').width()/9

  • SpringMvc 相关,bean map转换,百度天气,base64.js,jsBase64.java;

    1.Map<String,Object>与JavaBean[POJO,Model]转换; //model publicclassmodel{ privateintid; privateStringname; privateDatedateNow; publicintgetId(){ returnthis.id; } publicvoidsetId(intid){ this.id=id; } //.. } importorg.apache.commons.beanutils.BeanUtils; importjava.util.Date; //Map<String,Object>; Map<String,Object>map=newHashMap<String,Object>(); map.put("id","idxxxxxx"); map.put("name","name"); map.put("dateNow",newDate()); try{ BeanUtils.populate(model,map); }catch(Illeg

  • 浅拷贝与深拷贝

    我们可以先看看一个常遇到的一个小问题 leta={ age:1 } letb=a a.age=2 console.log(b.age)//2复制 从上面的例子中我们看到了,如果给一个变量赋值一个对象,那么两者的值会是同一个引用,其中一方变化,另一方也会有相应的改变。通常我们开发中不希望出现这样的问题,我们可以使用浅拷贝来解决这个问题。   此篇文章中也会简单阐述到栈堆,基本数据类型与引用数据类型,因为这些概念能更好的让你理解深拷贝与浅拷贝。 我们来举个浅拷贝例子: leta=[0,1,2,3,4], b=a; console.log(a===b); a[0]=1; console.log(a,b);复制   嗯?明明b复制了a,为啥修改数组a,数组b也跟着变了,这里我不禁陷入了沉思。 那么这里,就得引入基本数据类型与引用数据类型的概念了。 面试常问,基本数据类型有哪些,number,string,boolean,null,undefined,symbol以及未来ES10新增的BigInt(任意精度整数)七类。 引用数据类型(Object类)有常规名值对的无

  • linux执行jmeter脚本解决响应数据为空

    Linux服务器用命令执行了jmeter脚本,在本地查看结果时发现结果树种的“请求、响应数据”都显示为空,有错误日志中也看不出所以然,请看演示! 1,先执行脚本:执行成功(...endofrun),但是发现有两个错误 2, 从服务器到处rmw_*.jtl结果放在本地jmeter中查看,发现数据都显示空 3, 再查看jmeter.log日志,发现失败的请求并未有错误日志(当然验证的参数错误,并非配置等错误) 4,接下来怎么办呢?在jmeter.properties文件中修改配置 jmeter.save.saveservice.response_data=true jmeter.save.saveservice.samplerData=true 5, 在user.properties文件中追加配置 jmeter.save.saveservice.output_format=xmljmeter.save.saveservice.response_data=truejmeter.save.saveservice.samplerData=truejmeter

  • docker

    docker的三个点 1、image 2、container 3、artifactory

  • 使用Git客户端克隆项目时出现fatal: unable to access 错误的经历

    几经尝试才发现是自己的网络问题,因为自身dns的设置与他人的不同,没有用公司dns服务器,导致使用域名连接https://git.xxx.com这样的时候,解析的是外网的IP地址,而公司的DNS服务器指向的是一个192.168.X.X的内网地址,外网地址并没有开放网络接口造成无法访问,而出现上图中的错误  

相关推荐

推荐阅读