ASP.NET Core 8 MVC - 视图模型验证

问题描述 投票:0回答:2

我有一个 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,那将是多余的。

c# validation asp.net-core-mvc .net-8.0
2个回答
0
投票

您的问题不是模型验证问题,而是 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();

0
投票

您获得哪个属性的空引用? 其次,我认为您不需要指定 [AllowNull]。

© www.soinside.com 2019 - 2024. All rights reserved.