【问题标题】:`Model.plans.Count` threw an exception of type 'System.NullReferenceException``Model.plans.Count` 引发了“System.NullReferenceException”类型的异常
【发布时间】:2021-10-10 19:54:22
【问题描述】:

我正在尝试将字符串对象列表显示为复选框。我有两个项目,ASP.NET Core MVC 应用程序项目和 ASP.NET Core Web API 都在 5.0 NET Core 上运行。在 MVC 项目中,我正在调用 Web API 项目,它将在其中创建一个新的对象列表。然后在我的 MVC 项目中,我做了一个项目引用来调用视图中的控制器。当我运行应用程序时,MVC 将点击它调用 API 项目的行。在我的 API 控制器的 GetPlan() 方法中存储新创建的对象后,我尝试在我的 MVC 视图中将这些创建的对象显示为复选框。但是,当我这样做时,它说Model.plans.Count 抛出了System.NullReferenceException 类型的异常。然后再深入研究,它只会告诉我:

System.NullReferenceException: '对象引用未设置为对象的实例。'

Microsoft.AspNetCore.Mvc.Razor.RazorPage.Model.get 返回 null。

如果新对象已经创建,我不明白为什么它返回 null。

MVC 项目:

Controller 我在哪里调用我的 API:

 public IActionResult SecIndex()
    {
        // call planapi
        var result = _planServiceClient.RetrievePlans().Result;
        _logger.Log(LogLevel.Information, "Reached the Second Page View....");
        return View();
    }

然后是我的 API 项目:

Model class for ViewPhoneNumberInput:

  public class ViewPhoneNumberInput
    {
        [Required(ErrorMessage = "You did not enter your phone number! Please enter your phone number!")]
        public String PhoneNumber { get; set; }
        public List<Plans> plans { get; set; }
    }

我的计划模型类:

    public class Plans
    {
        public int PlanId { get; set; }
        public string PlanName { get; set; }
    }

Controller 用于 api:

        [HttpGet]
        [Route("AvailablePlans")]
        public ActionResult GetPlan()
        {

            var _model = new ViewPhoneNumberInput();
            _model.plans = new List<Plans>();

            _model.plans.Add(new Plans { PlanId = 1, PlanName = "Internet" });
            _model.plans.Add(new Plans { PlanId = 2, PlanName = "TV & Streaming" });
            _model.plans.Add(new Plans { PlanId = 3, PlanName = "Mobile" });
            _model.plans.Add(new Plans { PlanId = 4, PlanName = "Home Security" });
            _model.plans.Add(new Plans { PlanId = 5, PlanName = "Home Phone" });


            var plansAvailable = JsonSerializer.Serialize(_model.plans);
            _logger.Log(LogLevel.Information, "Response-Body: {@Response-Body}", plansAvailable);


            return Ok(_model.plans);
        }

_model.plans 以上将保存创建的新对象的值,因此我转到我的 MVC 视图项目来调用它,以便显示它,但这就是它说Model.plans.Count 抛出类型异常的地方'System.NullReferenceException`。

View 用于 MVC 项目:

@model Plan.API.Models.ViewPhoneNumberInput


<h2>Second page</h2>




@using (Html.BeginForm())
{

    for (int i = 0; i < Model.plans.Count; i++)
    {
        <input type="checkbox" id="@Model.plans[i].PlanId" name="Model.plans[i].PlanId" />
        <label for="Model.plans[i].PlanId"> @Model.plans[i].PlanName </label> <br />
    }


    @Html.TextBoxFor(r => r.PhoneNumber)
    <input id="Button" type="submit" value="Next" />

    <p>  @Html.ValidationMessageFor(r => r.PhoneNumber) </p>

}

为什么它告诉我而不是实际保存来自 API 控制器的新创建的值?


在实施以下建议时,我可以看到 PhoneNumber 的值,但对于计划,它显示为 null(当鼠标悬停在帖子 SecIndex 中的 phoneNumberInput 上时)。

我的检索计划():

        public async Task<List<Plan.API.Models.Plans>> RetrievePlans()
        {
            var response = await _httpClient.GetAsync("api/plans/AvailablePlans");
            response.EnsureSuccessStatusCode();

            var result = await response.Content.ReadAsStringAsync();

           var json = JsonConvert.DeserializeObject<List<Plan.API.Models.Plans>>(result);

            return json;
        }

我更新的SecIndex() with Post SecIndex()

        public IActionResult SecIndex()
        {
            // call planapi
            var result = _planServiceClient.RetrievePlans().Result;
            _logger.Log(LogLevel.Information, "Reached the Second Page View....");
            return View(new Plan.API.Models.ViewPhoneNumberInput { plans = result });//So that you don't need to change the model in the view and the parameter type in the HttpPost Action.
        }

        [HttpPost]
        public IActionResult SecIndex(Plan.API.Models.ViewPhoneNumberInput phoneNumberInput)
        {
            try
            {

                if (ModelState.IsValid)
                {
                    _logger.Log(LogLevel.Information, "User successfully entered their phone number.");
                    return RedirectToAction("FinalIndex", "Final");
                }
                else
                {
                    _logger.Log(LogLevel.Error, "User missed to enter their phone number");
                    throw new Exception("User did not enter a phone number in the textfield!");
                }
             
            }
            catch(Exception ex)
            {
                _logger.LogError(ex, "Empty textfield!");
                return View(phoneNumberInput);
            }
        }

SecIndex.cshtml

@model Plan.API.Models.ViewPhoneNumberInput

<h2>Second page</h2>


@using (Html.BeginForm())
{

    for (int i = 0; i < Model.plans.Count; i++)
    {
        <input type="checkbox" id="@Model.plans[i].PlanId" />
        <input hidden value="@Model.plans[i].PlanId" />
        <input hidden value="@Model.plans[i].PlanName" />

        <label for="Model.plans[i].PlanId"> @Model.plans[i].PlanName </label>
        <br />
    }


    @Html.TextBoxFor(r => r.PhoneNumber)
    <input id="Button" type="submit" value="Next" />

    <p>  @Html.ValidationMessageFor(r => r.PhoneNumber) </p>

}
<script>
    $('form').submit(function () {
        var count = 0;
        $('input[type=checkbox]').each(function () {
            if (this.checked) {
                this.nextElementSibling.name = "plans["+count+"].PlanId";
                this.nextElementSibling.nextElementSibling.name = "plans[" + count + "].PlanName";
                count++;
            }
        });
    });
</script>

我的 API GetPlan 控制器:

        [HttpGet]
        [Route("AvailablePlans")]
        public List<Plans> GetPlan()
        {

            var _model = new ViewPhoneNumberInput();
            _model.plans = new List<Plans>();

            _model.plans.Add(new Plans { PlanId = 1, PlanName = "Internet" });
            _model.plans.Add(new Plans { PlanId = 2, PlanName = "TV & Streaming" });
            _model.plans.Add(new Plans { PlanId = 3, PlanName = "Mobile" });
            _model.plans.Add(new Plans { PlanId = 4, PlanName = "Home Security" });
            _model.plans.Add(new Plans { PlanId = 5, PlanName = "Home Phone" });


            var plansAvailable = JsonSerializer.Serialize(_model.plans);
            _logger.Log(LogLevel.Information, "Response-Body: {@Response-Body}", plansAvailable);


            return _model.plans;
        }

【问题讨论】:

    标签: c# asp.net asp.net-mvc asp.net-core-mvc asp.net-core-webapi


    【解决方案1】:

    @MarCo 问题是你没有通过你的模态

    return View(result);
    

    【讨论】:

    • 我没有通过它,因为我对结果没有不同的看法。如果我在我的SecIndex() 中执行return View(result);,它会告诉我InvalidOperationException: The view '[{"planId":1,"planName":"Internet"},{"planId":2,"planName":"TV &amp; Streaming"},{"planId":3,"planName":"Mobile"},{"planId":4,"planName":"Home Security"},{"planId":5,"planName":"Home Phone"}]' was not found. The following locations were searched: 并给我检查的地方
    • @MarkCo 当您使用此行时返回 View(result);视图名称将由 SecIndex 的 ActionName 决定,并将结果传递给视图 SecIndex。
    • 我明白了,如果在控制器中我做了return Ok(_model.plans); 会保存新值,为什么它会返回?
    • @MarkCo return Ok(_model.Plans) 表示以明文形式返回数据,状态码为 200,主要用于 API 端。 return view(result) 意味着我们正在返回一个视图并将结果作为模型传递给我们将在视图中使用的模型。
    • 所以我必须专门为result 创建一个新视图?
    【解决方案2】:

    这是一个工作演示:

    api方法:

    [HttpGet]
            [Route("AvailablePlans")]
            public List<Plan.API.Models.Plans> GetPlan()
            {
    
                var _model = new ViewPhoneNumberInput();
                _model.plans = new List<Plans>();
    
                _model.plans.Add(new Plans { PlanId = 1, PlanName = "Internet" });
                _model.plans.Add(new Plans { PlanId = 2, PlanName = "TV & Streaming" });
                _model.plans.Add(new Plans { PlanId = 3, PlanName = "Mobile" });
                _model.plans.Add(new Plans { PlanId = 4, PlanName = "Home Security" });
                _model.plans.Add(new Plans { PlanId = 5, PlanName = "Home Phone" });
    
    
                var plansAvailable = JsonSerializer.Serialize(_model.plans);
                _logger.Log(LogLevel.Information, "Response-Body: {@Response-Body}", plansAvailable);
    
    
                return _model.plans;
            }
    

    控制器动作:

    public IActionResult SecIndex()
        {
            // call planapi
            var result = _planServiceClient.RetrievePlans().Result;
            _logger.Log(LogLevel.Information, "Reached the Second Page View....");
            return View(new ViewPhoneNumberInput {  plans=result});//So that you don't need to change the model in the view and the parameter type in the HttpPost Action.
        }
    

    查看:

    @model Plan.API.Models.ViewPhoneNumberInput
    
    
    <h2>Second page</h2>
    @using (Html.BeginForm())
    {
    
        for (int i = 0; i < Model.plans.Count; i++)
        {
            <input type="checkbox" id="@Model.plans[i].PlanId" />
            <input hidden value="@Model.plans[i].PlanId"/>
            <input hidden value="@Model.plans[i].PlanName" />
    
            <label for="Model.plans[i].PlanId"> @Model.plans[i].PlanName </label>
            <br />
        }
    
    
        @Html.TextBoxFor(r => r.PhoneNumber)
        <input id="Button" type="submit" value="Next" />
    
        <p>  @Html.ValidationMessageFor(r => r.PhoneNumber) </p>
    
    }
    <script>
        $('form').submit(function () {
            var count = 0;
            $('input[type=checkbox]').each(function () {
                if (this.checked) {
                    this.nextElementSibling.name = "plans["+count+"].PlanId";
                    this.nextElementSibling.nextElementSibling.name = "plans[" + count + "].PlanName";
                    count++;
                }
            });
        });
        </script>
    

    结果:

    【讨论】:

    • 谢谢!在尝试实施您的解决方案时,我在SecIndex() 的返回行遇到了问题。它有`return View(new Plan.API.Models.ViewPhoneNumberInput {plans = result });`它在结果下方有一条红线,上面写着“无法将类型'Plan.API.Models.ViewPhoneNumberInput`隐式转换为System.Collections.Generic.List&lt;Plan.API.Models.Plans&gt;。我也完全复制了您的解决方案,但我所做的唯一区别是在SecIndex() 的返回行中添加引用并删除GetPlan() 签名上的引用,因为API 中存在ViewPhoneNumberInput
    • 你是使用了帖子中实现的RetrievePlan()还是修改了它?
    • 请让我知道我是否正确实施了RetrievePlan(),因为当我将鼠标悬停在phoneNumberInput 上时,我可以看到PhoneNumber 中的值,但Plans 对我来说是空的。请检查帖子中的更新部分
    • RetrievePlan()返回List&lt;Plan.API.Models.Plans&gt;,所以使用return View(new ViewPhoneNumberInput { plans=result});时无法获取Cannot implicitly convert type 'Plan.API.Models.ViewPhoneNumberInput' to System.Collections.Generic.List&lt;Plan.API.Models.Plans&gt;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-14
    • 2013-08-28
    • 2015-07-18
    相关资源
    最近更新 更多