繁体   English   中英

为什么我不能使用 Entity Framework 提交带有导航属性的表单?

[英]Why can I not submit a form with a navigation property with Entity Framework?

我正在学习 C#,我的第一个项目围绕导航属性遇到了很多问题。 我遇到的主要障碍是基本的四个属性 model (以及包括导航属性的任何内容)。

这是我目前正在使用的一个:

公司 Model:

public class Company
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    [Display(Name = "Id")]
    public int Id { get; set; }
    [Display(Name = "Name")]
    public string Name {  get; set; }

    public CompanyType Type { get; set; }

    public ICollection<Contact>? Contacts { get; set; }
}

公司类型 Model:

public class CompanyType
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    [Display(Name = "Id")]
    public int Id { get; set; }
    [Display(Name = "Company Type")]
    public string Type { get; set; } = "";
}

这是我的背景——很简单。

    public class SRMContext : DbContext
    {

        public DbSet<Company> Companies { get; set; }
        public DbSet<Contact> Contacts { get; set; }
        public DbSet<CompanyType> CompanyTypes { get; set; }

        public SRMContext (DbContextOptions<SRMContext> options) : base(options)
        {
            
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Company>().ToTable("Companies");
            modelBuilder.Entity<Contact>().ToTable("Contacts");
            modelBuilder.Entity<CompanyType>().ToTable("CompanyTypes");
        }

    }

*我在上下文中删除了不相关的模型。

我首先使用脚手架为公司创建 CRUD razor 页面。 它根本不包含任何导航属性 - 对于任何 model。 不确定我是否做错了什么,或者这是否是标准的。 所以我试图手动更新脚手架页面以包含 CompanyType。 我能够生成一个 select 列表,并将其提供给 UI。 它给了我选项,我点击提交。 但是当我点击提交时,它说我需要包括公司类型。 如果我将其更改为不需要,它将提交,但不会列出任何公司类型。

我知道你可以只做 Id 而不做导航属性,但在我的代码中,我有多对多的导航属性,所以 Id 对它们不起作用。 我也遇到了同样的问题,但是在我弄清楚之前,这个问题要简单得多。

我究竟做错了什么?

在此处输入图像描述

在此处输入图像描述

public class CreateModel : PageModel
{
    private readonly SRMContext _context;
    public SelectList CompanyTypes { get; set; }

    public CreateModel(SRMContext context)
    {
        _context = context;
    }

    public async Task<IActionResult> OnGetAsync()
    {
        CompanyTypes = new SelectList(_context.CompanyTypes, nameof(CompanyType.Id), nameof(CompanyType.Type));
        return Page();
    }

    [BindProperty]
    public Company Company { get; set; }

    

    // To protect from overposting attacks, see https://aka.ms/RazorPagesCRUD
    public async Task<IActionResult> OnPostAsync()
    {
      if (!ModelState.IsValid)
        {
            return Page();
        }

        _context.Companies.Add(Company);
        await _context.SaveChangesAsync();

        return RedirectToPage("./Index");
    }
}

查看页面

<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="Company.Name" class="control-label"></label>
                <input asp-for="Company.Name" class="form-control" />
                <span asp-validation-for="Company.Name" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Company.Type" class="control-label"></label>
                <select asp-for="Company.Type" asp-items="Model.CompanyTypes" class="form-control"></select>
                <span asp-validation-for="Company.Type" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

(对不起,我知道它有很多代码,但我想展示所有需要的东西!)

表单帖子上无法识别复杂对象,因此类型可能是 null。 相反,发布该类型的外键,它应该被识别:

public class Company
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    [Key]
    [Display(Name = "Id")]
    public int Id { get; set; }

    [Display(Name = "Name")]
    public string Name {  get; set; }

    public int CompanyType_Id { get; set; }

    [ForeignKey("CompanyType_Id")]
    public CompanyType Type { get; set; }

    public ICollection<Contact>? Contacts { get; set; }
}

并以您的形式:

<select asp-for="Company.CompanyType_Id" asp-items="Model.CompanyTypes" class="form-control"></select>

没有测试过这个,但很确定这是问题所在。

请参阅我对这个问题的回答(将复杂实体内的复杂实体绑定到请求),以了解当您将实体发送到视图以充当其 Model 以及尝试发送该 model 时可能遇到的问题和风险时发生的情况的解释背部。

我给出的一般建议是不要将实体发送到视图,因为当服务器上的视图引擎获取实体时,从客户端浏览器返回的仍然不是实体,它只是被转换为实体的数据字段,所以它们要么不完整,要么容易被篡改。 (加上通常涉及通过网络发送比需要更多的数据)

您的 Create PageModel 是一个好的开始,但考虑只嵌入来自客户而不是客户实体的字段(更新/编辑也是如此)这将包括一个 CustomerTypeId 列,当您在 POST 调用中构建您的客户实体时,您获取来自 DbContext 的 CustomerType 引用并将其分配给新创建的客户。 这样做的好处是还可以验证您从客户端浏览器收到的任何 FK,以确保您处理的是合法数据,在比最终的SaveChanges调用更有用的调试位置抛出异常。 想要传递实体的典型理由是避免额外的 DB 读取命中。 通过 ID 读取这样的相关实体非常快,甚至在您可能需要加载多个相关实体的情况下,也有一些技术可以将它们批处理到单个读取操作中。 (选择所有需要的 ID,使用Contains一次性读取,然后从该集合中分配)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM