我正试图在我的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);
}
创建板
@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");
}
}
}
我认为您不会从设置名称中获得任何乐趣;即使上下文存在的时间足够长(它不应该存在;上下文应该只存在于对API的请求存在的时间内(,以看到你使用它以前下载的公司名称,它也会看到Guid.Empty(默认值(和(可能你已经告诉EF它是数据库生成的(,这会使上下文认为该公司是新的name 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实体(,你可以采用其中一种:
- 在保存之前按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);
}