我有一个 ASP.NET Core 8 MVC 应用程序。目前,我正在尝试让管理员能够为其电子商务网站创建新客户。他们有一个简单的形式,如下所示:
NewClient.cshtml
:
@using Ecommerce.Models
@model NewClientViewModel
@using Microsoft.AspNetCore.Identity
@using Ecommerce.ViewModel
<h1>New Client creation</h1>
<div>
<form asp-action="NewClient" asp-controller="Admin" method="post">
<div>
<label asp-for="Email">Email</label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div>
<label asp-for="FirstName">First Name</label>
<input asp-for="FirstName" class="form-control" />
<span asp-validation-for="FirstName" class="text-danger"></span>
</div>
<div>
<label asp-for="LastName">Last Name</label>
<input asp-for="LastName" class="form-control" />
<span asp-validation-for="LastName" class="text-danger"></span>
</div>
<div>
<label asp-for="Password">Password</label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div>
<label asp-for="BranchName">Branch Name</label>
<input asp-for="BranchName" class="form-control" />
<span asp-validation-for="BranchName" class="text-danger"></span>
</div>
<div>
<label asp-for="PhoneNumber">Phone Number</label>
<input asp-for="PhoneNumber" class="form-control" />
<span asp-validation-for="PhoneNumber" class="text-danger"></span>
</div>
<div>
<label asp-for="Address">Address</label>
<input asp-for="Address" class="form-control" />
<span asp-validation-for="Address" class="text-danger"></span>
</div>
<div class="form-check">
<input asp-for="CheckedBox" class="form-check-input" type="checkbox" id="flexCheckDefault">
<label class="form-check-label" for="flexCheckDefault">
If Address same as Shipping Address please check box here
</label>
</div>
<div>
<label asp-for="ShippingAddress">Shipping Address</label>
<input asp-for="ShippingAddress" class="form-control" />
<span asp-validation-for="ShippingAddress" class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Create Client</button>
</div>
</form>
</div>
在复选框上,如果地址与送货地址相同,则应将复选框选中为“true”。这是模型、视图模型和处理 post 请求的控制器:
NewClientViewModel
:
[Required]
public string Email { get; set; }
[Required]
[Display(Name = "First Name")]
public string FirstName { get; set; }
[Required]
[Display(Name = "Last Name")]
public string LastName { get; set;}
[Required]
public string Password { get; set; }
[Required]
[Display(Name = "Branch Name")]
public string BranchName { get; set; }
[Required]
[Display(Name = "Phone Number")]
public string PhoneNumber { get; set; }
[Required]
public string Address { get; set; }
[AllowNull]
[Display(Name = "Shipping Address")]
public string? ShippingAddress { get; set; }
public bool CheckedBox { get; set; } = false;
Client.cs
:
public class Client
{
public int Id { get; set; }
public string BranchName { get; set; }
public string PhoneNumber { get; set; }
public string Address { get; set; }
[AllowNull] // <-- added this to test out if this attribute would work. It didn't make a difference
// By adding the question after the type it should allow null to my understanding
public string? ShippingAddress { get; set; }
}
AdminController
:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> NewClient(NewClientViewModel client)
{
if (!ModelState.IsValid)
return View();
User newClientUser = new User
{
UserName = client.Email,
Email = client.Email,
FirstName = client.FirstName,
LastName = client.LastName
};
Client newClient = new Client();
// So if the user checks box true we enter this if check
// to set shipping address null. Otherwise do the opposite
if (client.CheckedBox)
{
newClient.BranchName = client.BranchName;
newClient.PhoneNumber = client.PhoneNumber;
newClient.Address = client.Address;
newClient.ShippingAddress = null;
}
else
{
newClient.PhoneNumber = client.PhoneNumber;
newClient.Address = client.Address;
newClient.ShippingAddress = client.ShippingAddress;
}
IdentityResult result = await _userManager.CreateAsync(newClientUser, client.Password);
if (!result.Succeeded)
{
_logger.LogInformation("Failed attempt");
ModelState.AddModelError(string.Empty, "Hmm something went wrong!");
return View();
}
await _userManager.AddToRoleAsync(newClientUser, Roles.Customer.ToString());
// This line throws a null ref exception
await _dbContext.Clients.AddAsync(newClient);
await _dbContext.SaveChangesAsync();
return RedirectToAction("Dashboard", "Admin");
}
我的问题是,如果我将客户端模型和视图模型设置为允许 null,为什么我会得到 null 引用。我还在数据库中验证,如果未提供任何内容,它将接受 null。我忽略了什么,它无法将新客户端保存到数据库中?我可以明确提供
newClient.ShippingAddress = null;
到 newClient.ShippingAddress = client.ShippingAddress;
,但是,我觉得如果我在模型中说接受 null,那将是多余的。
您的问题不是模型验证问题,而是 EFCore 问题
既然你已经确认了
我还在数据库中验证了如果没有任何内容,它将接受 null 提供。
// The line below gives me a null ref exception
await _dbContext.Clients.AddAsync(newClient);
由于实体之间的关系,您会得到空引用异常
您可以查看此问题相关内容和文档以了解如何使用EFCore保存相关数据
var entity = context.SomeEntity.Include(b => b.Clients).First();
var client = new Client { ..... };
entity.clients.Add(client);
context.SaveChanges();
您获得哪个属性的空引用? 其次,我认为您不需要指定 [AllowNull]。