【问题标题】:Creating a complex object using Web Api in Blazor在 Blazor 中使用 Web Api 创建复杂对象
【发布时间】:2022-01-26 01:45:06
【问题描述】:

我正在尝试使用我的 web api 中的 Post 方法创建一个复杂的对象。但是,我很难做到这一点,因为当我创建一个 Board 对象时,我要求它有一个 Board.Company.Name 将它与公司相关联。但是,当我选择一个已经存在的公司名称并处理有效提交时,会使用我选择的 Board.Company.Name 创建一个新公司。然后我显示我创建的董事会,看起来没有公司实际上与它相关联。下面我已经包含了相关代码。这是我第一个使用 C# 和 Blazor 的项目,所以如果我遗漏了任何重要的内容,请告诉我,我会包括在内。

公司模式
public class Company
    {
        [Key]
        public Guid Id { get; set; }

        [Required]
        public string Name { get; set; }

        public DateTime Founded { get; set; }
    }
板型
public class Board
    {
        [Key]
        public Guid Id { get; set; }

        [ValidateComplexType]
        public Company Company { get; set; } = new();

        [Required]
        public string Name { get; set; }

        public string Description { get; set; }

        public List<Ticket> Tickets { get; set; }

        public Board()
        {
        }
    }
API POST 方法
[HttpPost]
public async Task<ActionResult<Board>> PostBoard(Board board)
{
    _context.Boards.Add(board);
    await _context.SaveChangesAsync();

    return CreatedAtAction("GetBoard", new { id = board.Id }, board);
}
创建_Board
@page "/create_board"

@inject NavigationManager Navigation
@inject HttpClient Http

<div>
    <button class="btn btn-outline-secondary oi oi-arrow-left" @onclick="GoToHome"></button>
    <h3 class="text-center">Create a board</h3>
</div>

<hr />

<EditForm Model="Board" OnValidSubmit="@HandleValidSubmit">
    <ObjectGraphDataAnnotationsValidator />

    <div class="form-group row">
        <label for="Company" class="col-sm-2 col-form-label">Company</label>
        <div class="col-sm-10">
            <InputSelect id="Company" class="form-control" @bind-Value="Board.Company.Name">
                <option value="" disabled selected>Company</option>
                @foreach (var company in Companies)
                {
                    <option>@company.Value.Name</option>
                }
            </InputSelect>
            <ValidationMessage For="@(() => Board.Company.Name)" />
        </div>
    </div>

    <div class="form-group row">
        <label for="Name" class="col-sm-2 col-form-label">Name</label>
        <div class="col-sm-10">
            <InputText id="Name" class="form-control" placeholder="Name" @bind-Value="Board.Name" />
            <ValidationMessage For="@(() => Board.Name)" />
        </div>
    </div>

    <div class="form-group row">
        <label for="Description" class="col-sm-2 col-form-label">Description</label>
        <div class="col-sm-10">
            <InputText id="Description" class="form-control" placeholder="Description" @bind-Value="Board.Description" />
            <ValidationMessage For="@(() => Board.Description)" />
        </div>
    </div>

    <button type="submit" class="btn btn-primary">Submit</button>
</EditForm>

@code {
    private void GoToHome()
    {
        Navigation.NavigateTo("/");
    }

    private Board Board { get; set; } = new Board();

    private Dictionary<Guid, Company> Companies = new Dictionary<Guid, Company>();

    protected override async Task OnInitializedAsync()
    {
        try
        {
            Companies = await Http.GetFromJsonAsync<Dictionary<Guid, Company>>("api/Companies");
        }
        catch (Exception)
        {
            Console.WriteLine("Exception occurred for GET companies");
        }
    }

    private async void HandleValidSubmit()
    {
        try
        {
            var response = await Http.PostAsJsonAsync("/api/Boards", Board);
            response.EnsureSuccessStatusCode();

            var content = await response.Content.ReadAsStringAsync();
            var board = JsonConvert.DeserializeObject<Board>(content);
            Navigation.NavigateTo($"/read_board/{board.Id}");
        }
        catch (Exception)
        {

            Console.WriteLine("Exception occurred for POST board");
        }
    }
}

【问题讨论】:

    标签: c# api object constructor blazor


    【解决方案1】:

    我不认为你会从设置名称中获得任何乐趣;即使上下文存在足够长的时间(它不应该;上下文应该只存在于对 API 的请求的时间)以看到您使用之前下载的公司名称,它也会看到 Guid.Empty(默认)和(大概你已经告诉 EF 它是由数据库生成的)这将使上下文认为该公司是新公司,名称为 X

    相反,我认为我应该让实体遵循典型的“让 CompanyId 成为董事会成员并将其设置在那里”的路线,而不是在新的相关实体上设置名称:

    <InputSelect id="Company" class="form-control" @bind-Value="Board.CompanyId">
        <option value="" disabled selected>Company</option>
        @foreach (var company in Companies)
        {
            <option value="@company.Key">@company.Value.Name</option>
        }
    </InputSelect>
    
    

    这应该会保存,EF 会看到公司 ID 并连接相关公司。

    如果您反对这一点(将 CompanyId 实体添加到 Board),您可以采用以下任一方法:

    • 在保存之前按 ID 下载该公司,并将其指定为公司 - 然后您将使用更改跟踪器之前看到的公司实例,它会知道如何连接到现有公司,而不是创建一个新的例如
    <InputSelect id="Company" class="form-control" @bind-Value="Board.Company.Id">
        <option value="" disabled selected>Company</option>
        @foreach (var company in Companies)
        {
            <option value="@company.Key">@company.Value.Name</option>
        }
    </InputSelect>
    
    [HttpPost]
    public async Task<ActionResult<Board>> PostBoard(Board board)
    {
        board.Company = _context.Companies.Find(board.Company.Id); // download existing co with that ID
        _context.Boards.Add(board);
        await _context.SaveChangesAsync();
    
        return CreatedAtAction("GetBoard", new { id = board.Id }, board);
    }
    

    • 让变更跟踪器/上下文认为它已经看到了您使用 ID X 创建的新公司。就我个人而言,我不是粉丝,但是:
    [HttpPost]
    public async Task<ActionResult<Board>> PostBoard(Board board)
    {
        _context.Boards.Add(board);
        _context.Entries(board.Company).State = EntityState.Unchanged; //don't try to save the Company
    
        await _context.SaveChangesAsync();
    
        return CreatedAtAction("GetBoard", new { id = board.Id }, board);
    }
    

    【讨论】:

    • 我使用了这个例子,只将 value="@Company.Key" 更改为 value="@company.Key" 因为我需要在字典中引用公司而不是 Company 类否则我会收到一个错误。但是,当我选择表单上的提交按钮时,没有任何反应?不知道为什么,没有例外,没有有效提交的迹象,什么都没有。
    • 验证器阻止提交?您曾经将验证器连接到名称,现在它将是 ID - 是某个验证器阻止提交,因为它希望填写一个名称,现在它是空白的?
    • 顺便发布了一些关于节省的额外建议
    • 谢谢。我决定在 Board 中添加一个 CompanyId 实体,并且效果很好。现在一切正常。
    • 没问题!如果您不再寻找此特定问题的答案,您可以勾选向上/向下按钮下的灰色复选标记以将其变为绿色。这会更改仪表板上的问题外观,让其他人知道它已得到回答跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-09
    • 1970-01-01
    • 2012-01-20
    • 1970-01-01
    相关资源
    最近更新 更多