Blazor: Исключение NullReferenceException в _Host.cshtml при загрузке страницы с компонентом EditForm

#c# #asp.net #.net #blazor #blazor-server-side

Вопрос:

Я разрабатываю серверное приложение Blazor, которое использует ядро EntityFramework для создания базы данных (сначала код). Я хочу использовать одну из моделей в компоненте EditForm, но она вызывает исключение NullReferenceException в _Host.cshtml всякий раз, когда я открываю страницу, содержащую компонент EditForm. Другие страницы работают совершенно нормально! Ошибка на скриншоте Райдера Это ошибка, приведенная на странице:

 An unhandled exception occurred while processing the request.
NullReferenceException: Object reference not set to an instance of an object.
WebApplication.Pages.Components.Customer.EditCustomer.<BuildRenderTree>b__0_4(RenderTreeBuilder __builder3)
Stack Query Cookies Headers Routing
NullReferenceException: Object reference not set to an instance of an object.
WebApplication.Pages.Components.Customer.EditCustomer.<BuildRenderTree>b__0_4(RenderTreeBuilder __builder3)
Microsoft.AspNetCore.Components.CascadingValue<TValue>.Render(RenderTreeBuilder builder)
Microsoft.AspNetCore.Components.Rendering.ComponentState.RenderIntoBatch(RenderBatchBuilder batchBuilder, RenderFragment renderFragment)
Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderInExistingBatch(RenderQueueEntry renderQueueEntry)
Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()
Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.HandleException(Exception exception)
Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessRenderQueue()
Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessPendingRender()
Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(int componentId, RenderFragment renderFragment)
Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged()
Microsoft.AspNetCore.Components.ComponentBase.CallOnParametersSetAsync()
Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.HandleException(Exception exception)
Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToPendingTasks(Task task)
Microsoft.AspNetCore.Components.Rendering.ComponentState.SetDirectParameters(ParameterView parameters)
Microsoft.AspNetCore.Components.RenderTree.Renderer.RenderRootComponentAsync(int componentId, ParameterView initialParameters)
Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.CreateInitialRenderAsync(Type componentType, ParameterView initialParameters)
Microsoft.AspNetCore.Components.Rendering.HtmlRenderer.RenderComponentAsync(Type componentType, ParameterView initialParameters)
Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContext <>c__11<TResult> <<InvokeAsync>b__11_0>d.MoveNext()
Microsoft.AspNetCore.Mvc.ViewFeatures.StaticComponentRenderer.PrerenderComponentAsync(ParameterView parameters, HttpContext httpContext, Type componentType)
Microsoft.AspNetCore.Mvc.ViewFeatures.ComponentRenderer.PrerenderedServerComponentAsync(HttpContext context, ServerComponentInvocationSequence invocationId, Type type, ParameterView parametersCollection)
Microsoft.AspNetCore.Mvc.ViewFeatures.ComponentRenderer.RenderComponentAsync(ViewContext viewContext, Type componentType, RenderMode renderMode, object parameters)
Microsoft.AspNetCore.Mvc.TagHelpers.ComponentTagHelper.ProcessAsync(TagHelperContext context, TagHelperOutput output)
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperRunner.<RunAsync>g__Awaited|0_0(Task task, TagHelperExecutionContext executionContext, int i, int count)
WebApplication.Pages.Pages__Host.<ExecuteAsync>b__14_1() in _Host.cshtml
<component type="typeof(App)" render-mode="ServerPrerendered"/>
Microsoft.AspNetCore.Razor.Runtime.TagHelpers.TagHelperExecutionContext.SetOutputContentAsync()
WebApplication.Pages.Pages__Host.ExecuteAsync() in _Host.cshtml
    Layout = null;
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageCoreAsync(IRazorPage page, ViewContext context)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderPageAsync(IRazorPage page, ViewContext context, bool invokeViewStarts)
Microsoft.AspNetCore.Mvc.Razor.RazorView.RenderAsync(ViewContext context)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor.ExecuteAsync(ViewContext viewContext, string contentType, Nullable<int> statusCode)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0<TFilter, TFilterAsync>(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext<TFilter, TFilterAsync>(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeResultFilters()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
Show raw exception details
 

Это мой редактор-компонент Razor:

 @page "/customers/edit/"
@page "/customers/edit/{CustomerId:int}"
@using DataModels.Invoice
@inject IToastService _toastService

<AuthorizeView Context="authContext">
    <Authorized>
        <h3>Edit Customer</h3>
        <EditForm OnValidSubmit="@OnValid_Submit" Context="formContext" Model="@CurrentModel">
            <DataAnnotationsValidator/>
            <ValidationSummary/>
            <div class="form-group">
                <label for="CompanyName">Company Name:</label>
                <input id="CompanyName" class="form-control" type="text"/>
            </div>
            <div class="form-row">
                <div class="form-group col-md-1">
                    <label for="FormOfAddress">Form:</label>
                    <select id="FormOfAddress" class="form-control">
                        <option value="1">Herr</option>
                        <option value="2">Frau</option>
                        <option value="3">Divers</option>
                    </select>
                </div>
                <div class="form-group col-md-3">
                    <label for="PreName">Prename:</label>
                    <input id="PreName" class="form-control" type="text"/>
                </div>
                <div class="form-group col-md-3">
                    <label for="SurName">Surname:</label>
                    <input id="SurName" class="form-control" type="text"/>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group col-md-3">
                    <label for="Email">Email:</label>
                    <input id="Email" class="form-control" type="email"/>
                </div>
                <div class="form-group col-md-3">
                    <label for="Telephone">Telephone:</label>
                    <input id="Telephone" class="form-control" type="tel"/>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group col-md-6">
                    <label for="Address">Address:</label>
                    <input id="Address"
                           @bind="CurrentModel.Address.StreetName"
                           class="form-control"
                           type="text"/>
                </div>
                <div class="form-group col-md-1">
                    <label for="HouseNumber">Nr:</label>
                    <input id="HouseNumber"
                           @bind="CurrentModel.Address.HouseNumber"
                           class="form-control"
                           type="text"/>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group col-md-2">
                    <label for="ZipCode">Zip-Code:</label>
                    <input id="ZipCode"
                           @bind="CurrentModel.Address.City.ZipCode"
                           class="form-control"
                           type="text"/>
                </div>
                <div class="form-group col-md-3">
                    <label for="City">City:</label>
                    <input id="City"
                           @bind="CurrentModel.Address.City.Name"
                           class="form-control"
                           type="text"/>
                </div>
            </div>
            <div class="form-row">
                <div class="form-group col-md-3">
                    <label for="UstId">Ust.Id.:</label>
                    <input id="UstId" class="form-control" type="text"/>
                </div>
                <div class="form-group col-md-3">
                    <label for="CommercialRegisterNr">Commercial Register Nr.:</label>
                    <input id="CommercialRegisterNr" class="form-control" type="text"/>
                </div>
            </div>
            <div class="form-group">
                <h4>Fields</h4>
                <table class="table">
                    <thead>
                    <tr>
                        <th>Key</th>
                        <th>Value</th>
                        <th>Action</th>
                    </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </div>
            <button type="submit" class="btn btn-primary">Save Changes</button>
        </EditForm>
    </Authorized>
    <NotAuthorized>
        <NotPermitted/>
    </NotAuthorized>
</AuthorizeView>
 

C# code:

 @code {

    [Parameter]
    public int? CustomerId { get; set; }

    private Customer CurrentModel { get; set; } = new();

    private List<Tuple<string, string>> Fields { get; set; }

    private string TupleKey { get; set; }
    private string TupleValue { get; set; }

    protected override void OnInitialized()
    {
        Fields = new List<Tuple<string, string>>();
        if (CustomerId is not null)
        {
    //Load from DbContext
            return;
        }
        CurrentModel = new Customer();
    }

    private void OnValid_Submit(EditContext context)
    {
        Console.WriteLine("works.");
    }

}
 

Это моя клиентская модель:

 using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace DataModels.Invoice
{
    public class Customer
    {

        [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        public string CompanyName { get; set; }
        public ContactPerson ContactPerson { get; set; }
        public Address Address { get; set; }
        public string UstId { get; set; }
        public string CommercialRegisterNr { get; set; }
        public IEnumerable<CustomerInfoField>? AdditionalFields { get; set; }
        public IEnumerable<Invoice>? Invoices { get; set; }
    }
}
 

Страница работает при добавлении «фиктивного»класса с одним или двумя свойствами в нем с помощью @bind.
Был бы очень признателен вам за помощь, так как это сводит меня с ума…

Комментарии:

1. Да, к сожалению, ошибки во время рендеринга очень трудно отлаживать, так как они не указывают, в чем именно в вашем исходном коде кроется проблема. Лучше всего начать с рабочего состояния и добавлять вещи обратно, пока оно не сломается, или наоборот.

2. @KirkWoll да, я просто продолжал пытаться, я обнаружил, что проблема, похоже, в том, что класс Customer содержит другие классы (т. Е. адрес, контактное лицо) со свойствами, которые я хочу связать. При создании нового объекта с помощью формы редактирования создается впечатление, что привязке (at)не нравятся «несуществующие» классы. Потому что форма, похоже, работает, когда я загружаю данные через EF с помощью команд включения. Когда я звоню клиенту. ContactPerson = новый КонтактПерсон() и т. Д. В методе OnInitialized это также, похоже, работает. Но это не объясняет, почему исключение создается в хосте.

3. Да, вы захотите, чтобы объекты, к которым вы привязываетесь, не были пустыми. Что касается жалоб на «Хост», то это просто из-за того, что это точка входа верхнего уровня. Верхняя часть вашей трассировки стека EditCustomer.<BuildRenderTree>b__0_4(RenderTreeBuilder __builder3) , что немного более полезно, хотя, к сожалению, нет номеров строк.

Ответ №1:

я вроде как нашел причину такого поведения. Объекты, содержащиеся в классе Customer, т. е. контактное лицо, кажутся пустыми, когда привязка пуста. При использовании DbContext для получения существующего объекта с помощью .Включить-Вызывает работу страницы. На данный момент это мой обходной путь:

 protected override async Task OnInitializedAsync()
    {
        Fields = new List<Tuple<string, string>>();
        if (CustomerId is not null)
        {
            await using var context = new ApplicationDbContext();
            CurrentModel = await context.Customers
                .Include(x => x.ContactPerson)
                .Include(x => x.Address)
                .Include(x => x.AdditionalFields)
                .FirstOrDefaultAsync(c => c.Id == CustomerId);
            return;
        }
        CurrentModel = Customer.CreateEmpty();
    }
 

И статический метод для новых объектов:

 public static Customer CreateEmpty()
        {
            var customer = new Customer
            {
                ContactPerson = new ContactPerson()
            };
            var address = new Address
            {
                City = new City()
            };
            customer.Address = address;
            customer.AdditionalFields = new List<CustomerInfoField>();
            return customer;
        }
 

Комментарии:

1. Я думаю, вы сами ответили на свой вопрос. Вам нужно явно перейти к вложенным объектам в вашем наборе данных. В вашем оригинале, @bind="CurrentModel.Address.HouseNumber" например, пытались привязаться к нулевому Address объекту., следовательно, «Ссылка на объект не установлена на экземпляр объекта».