You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core/.NET Core中Identity表TPH多用户继承登录报错排查

TPH继承下ApplicationUser登录触发SqlNullValueException问题排查

项目背景与代码实现

基于.NET Core + SQL Server,采用数据库优先模式实现TPH表继承,实体类定义如下:

public class ApplicationUser: IdentityUser
{       
    public string FirstName { get; set; }
    public string LastName { get; set; }
}  

public class Employer : ApplicationUser
{     
    public string employerName { get; set; }
    public string employerWebsite { get; set; } 
}

HomeController中创建Employer的代码:

private readonly UserManager<Employer> customerManager; // 注:原代码中UserManager<Application>为笔误

public HomeController(UserManager<Employer> _customerManager)
{           
    customerManager = _customerManager;
}  
  
public async Task<ActionResult> IndexAsync()
{
    var emp = new Employer() 
                  { 
                      Email = "customer1@hotmail.com", 
                      UserName = "Tom", 
                      employerName = "Harry",
                      employerWebsite = "ghfhgf@hhh.com"
                  };

    var result = await customerManager.CreateAsync(emp, "Password01!");          
}      

问题现象

创建Employer实例时功能正常,AspNetUsers表中Discriminator列正确填充,但使用SignInManager<ApplicationUser>登录时抛出以下异常:

SqlNullValueException: Data is Null. This method or property cannot be called on Null values.  
Microsoft.Data.SqlClient.SqlDataReader.GetString(int i)
...
Microsoft.AspNetCore.Identity.SignInManager<TUser>.PasswordSignInAsync(string userName, string password, bool isPersistent, bool lockoutOnFailure)  
ArevnaHireWebApp.Areas.Identity.Pages.Account.LoginModel.OnPostAsync(string returnUrl) in Login.cshtml.cs  
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);

登录页核心代码:

public class LoginModel : PageModel
{
    private readonly SignInManager<ApplicationUser> _signInManager;
    private readonly ILogger<LoginModel> _logger;

    public LoginModel(SignInManager<ApplicationUser> signInManager, ILogger<LoginModel> logger)
    {
        _signInManager = signInManager;
        _logger = logger;
    }

    public class InputModel
    {
        [Display(Name = "Title")]
        [Required(ErrorMessage = "Title is required")]
        public int TitleID { get; set; }

        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "First Name")]
        public string FirstName { get; set; }

        [Required]
        [DataType(DataType.Text)]
        [Display(Name = "Last Name")]
        public string LastName { get; set; }

        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string Email { get; set; }

        [Required]
        [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }

        [DataType(DataType.Password)]
        [Display(Name = "Confirm password")]
        [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
        public string ConfirmPassword { get; set; }
    }

    public async Task<IActionResult> OnPostAsync(string returnUrl = null)
    {
        returnUrl ??= Url.Content("~/");

        ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();

        if (ModelState.IsValid)
        {
            var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
        }
        // 省略后续代码
    }
}

原因分析

  1. 核心触发点:ApplicationUser中的FirstName和LastName为非可空字符串(.NET 6+默认开启Nullable参考类型,string默认不可空),但创建Employer时未给这两个字段赋值,导致数据库中对应字段为NULL。
  2. 登录时的冲突:SignInManager<ApplicationUser>查询用户数据时,EF Core尝试将数据库中的NULL值赋值给非可空的FirstName/LastName属性,触发SqlNullValueException。

解决方法

方法1:将ApplicationUser的字段改为可空类型

修改ApplicationUser类,标记FirstName和LastName为可空:

public class ApplicationUser: IdentityUser
{       
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}  
  • 数据库优先模式下,需确保数据库中这两个字段允许NULL;若使用Code-First,需生成并执行迁移更新数据库结构。

方法2:创建Employer时强制赋值FirstName和LastName

修改创建逻辑,确保必填字段被赋值:

var emp = new Employer() 
{ 
    Email = "customer1@hotmail.com", 
    UserName = "Tom", 
    FirstName = "Tom",
    LastName = "Smith",
    employerName = "Harry",
    employerWebsite = "ghfhgf@hhh.com"
};

方法3:关闭Nullable参考类型(不推荐)

在项目的.csproj文件中关闭Nullable检查:

<PropertyGroup>
  <Nullable>disable</Nullable>
</PropertyGroup>

此方法会失去编译时空引用检查能力,仅作为临时解决方案。

内容的提问来源于stack exchange,提问作者Titan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 08:53:14