TryUpdateModelAsync拒绝空导航属性?

huangapple go评论53阅读模式
英文:

TryUpdateModelAsync rejects null naviagation properties?

问题

TryUpdateModelAsync()似乎会拒绝我的模型对象,如果导航属性为空,即使外键已正确设置。

例如,如果AuthorId已设置但Author为空,它将不会接受一个Book对象:

public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public int AuthorId { get; set; }
    public Author Author { get; set; }
}

以下是一个完整的工作示例,显示了这种行为的问题:

public class Author
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Book> Books { get; set; }
}

模型:

public class EditModel : PageModel
{
    private readonly YourDbContext _context;
    private readonly ILogger<EditModel> _logger;

    [BindProperty] public Book Book { get; set; }
    public List<Author> Authors { get; set; }

    public EditModel(YourDbContext context, ILogger<EditModel> logger)
    {
        _context = context;
        _logger = logger;
    }

    public async Task<IActionResult> OnGetAsync(int? id)
    {
        if (id == null) { return NotFound(); }

        Book = await _context.Books.Include(b => b.Author).FirstOrDefaultAsync(m => m.Id == id);
        if (Book == null) { return NotFound(); }

        Authors = await _context.Authors.ToListAsync();
        return Page();
    }

    public async Task<IActionResult> OnPostAsync(int? id)
    {
        var bookToUpdate = await _context.Books.FindAsync(id);
        if (bookToUpdate == null) { return NotFound(); }

        if (await TryUpdateModelAsync<Book>(bookToUpdate, "book", b => b.Title, b => b.AuthorId))
        {
            await _context.SaveChangesAsync();
            return RedirectToPage("./Index");
        }
        else 
        {
            foreach (var modelStateKey in ModelState.Keys)
            {
                var modelStateVal = ModelState[modelStateKey];
                foreach (var error in modelStateVal.Errors)
                {
                    _logger.LogError($"Key: {modelStateKey}, Error: {error.ErrorMessage}");
                }
            }
        }
        Book = bookToUpdate;
        return RedirectToPage(new { id });
    }
}

Razor Page:

@page
@model EditModel

<form method="post">
    <input type="hidden" asp-for="Book.Id" />
    <input asp-for="Book.Title" />
    <select asp-for="Book.AuthorId" asp-items="@(new SelectList(Model.Authors, "Id", "Name"))"></select>
    <input type="submit" value="Save" />
</form>

模型状态将始终包含以下错误:

Key: Book.Author, Error: The Author field is required.

这显然是一个简化的示例,但通常可能有许多大型导航属性,因此无法填充它们。

英文:

TryUpdateModelAsync() seems to reject my model objects if the navigation property is null, even though the foreign key is properly set.

For example, it won't accept a Book if AuthorId is set, but Author is null:

public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public int AuthorId { get; set; }
    public Author Author { get; set; }
}

Here is a complete working example showing the problem of this behavior:

public class Author
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List&lt;Book&gt; Books { get; set; }
}

Model:

public class EditModel : PageModel
{
    private readonly YourDbContext _context;
    private readonly ILogger&lt;EditModel&gt; _logger;

    [BindProperty] public Book Book { get; set; }
    public List&lt;Author&gt; Authors {get; set;}

    public EditModel(YourDbContext context, ILogger&lt;EditModel&gt; logger)
    {
        _context = context;
        _logger = logger;
    }

    public async Task&lt;IActionResult&gt; OnGetAsync(int? id)
    {
        if (id == null) { return NotFound(); }

        Book = await _context.Books.Include(b =&gt; b.Author).FirstOrDefaultAsync(m =&gt; m.Id == id);
        if (Book == null) { return NotFound(); }

        Authors = await _context.Authors.ToListAsync();
        return Page();
    }

    public async Task&lt;IActionResult&gt; OnPostAsync(int? id)
    {
        var bookToUpdate = await _context.Books.FindAsync(id);
        if (bookToUpdate == null) { return NotFound(); }

        if (await TryUpdateModelAsync&lt;Book&gt;(bookToUpdate, &quot;book&quot;, b =&gt; b.Title, b =&gt; b.AuthorId))
        {
            await _context.SaveChangesAsync();
            return RedirectToPage(&quot;./Index&quot;);
        }
        else 
        {
            foreach (var modelStateKey in ModelState.Keys)
            {
                var modelStateVal = ModelState[modelStateKey];
                foreach (var error in modelStateVal.Errors)
                {
                    _logger.LogError($&quot;Key: {modelStateKey}, Error: {error.ErrorMessage}&quot;);
                }
            }
        }
        Book = bookToUpdate;
        return RedirectToPage(new { id});
    }
}

Razor Page:

@page
@model EditModel

&lt;form method=&quot;post&quot;&gt;
    &lt;input type=&quot;hidden&quot; asp-for=&quot;Book.Id&quot; /&gt;
    &lt;input asp-for=&quot;Book.Title&quot; /&gt;
    &lt;select asp-for=&quot;Book.AuthorId&quot; asp-items=&quot;@(new SelectList(Model.Authors,&quot;Id&quot;,&quot;Name&quot;))&quot;&gt;&lt;/select&gt;
    &lt;input type=&quot;submit&quot; value=&quot;Save&quot; /&gt;
&lt;/form&gt;

The modelstate will always have errors complaining:

Key: Book.Author, Error: The Author field is required.

This is obviously a simplified example, but in general there can be many large navigation properties, and so populating them all is not feasible.

答案1

得分: 1

在 .Net 6 或更高版本中,它会自动在您的 csproj 文件中启用可空引用类型:

<Nullable>enable</Nullable>

属性

public Author Author { get; set; }

将被视为非可空

正如在文档中提到的:

验证系统会将非可空参数或绑定属性视为具有 [Required(AllowEmptyStrings = true)] 特性。

如果要保持其为非可空,对于 MVC 项目的解决方案如下:

builder.Services.AddControllers(
    options => options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

在 Razor Page 项目中,您可以尝试:

builder.Services.Configure<MvcOptions>(x => x.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

我在我的一侧尝试过,它可以正常工作:
TryUpdateModelAsync拒绝空导航属性?

英文:

In .Net 6 or higher versions, it would enable nullable reference types atuomaticlly in your csproj file:

&lt;Nullable&gt;enable&lt;/Nullable&gt;

The property

public Author Author { get; set; }

would be considered as non-nullable

And as mentioned in the document:

> The validation system treats non-nullable parameters or bound
> properties as if they had a [Required(AllowEmptyStrings = true)]
> attribute.

If you want to keep it as non-nullable,the solution for MVC projects:

builder.Services.AddControllers(
    options =&gt; options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true);

In Razor Page projects,you could try:

builder.Services.Configure&lt;MvcOptions&gt;(x=&gt;x.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes=true);

I tried on myside and it works :
TryUpdateModelAsync拒绝空导航属性?

huangapple
  • 本文由 发表于 2023年6月15日 04:22:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/76477275.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定