Asp .Net Core 3.1 Razor 页面:复杂模型绑定

Posted

技术标签:

【中文标题】Asp .Net Core 3.1 Razor 页面:复杂模型绑定【英文标题】:Asp .Net Core 3.1 Razor Page: Complex model binding 【发布时间】:2020-11-16 22:24:03 【问题描述】:

我在数据绑定和 Razor 页面上苦苦挣扎

我无法找到解决方案。可能我问的问题不正确。 所以我希望你能帮我指出正确的方向。

我想要一个页面来创建预订以收集所有必要的数据,以便在一切完成后将其发布到 Web api。

在该页面上,我想为 wooking 收集必要的数据,并希望将 x 预订详细信息添加到预订中。

数据模型

public class Booking

   [Key]
   public int Id  get; set; 

   public string Reference  get; set; 

   public List<BookingDetail> BookingDetails  get; set; 

public class BookingDetail

   [Key]
   public int Id  get; set; 

   public int Pieces  get; set; 

   public string Marks  get; set; 

我想做的是为预订创建一个创建页面。 到目前为止,我遵循了这个说明,它正在工作Link

这个缺失的部分是我想在同一个 Razor 页面上添加新的预订详细信息。

所以我想查看所有添加的预订详细信息的列表,并且我希望有文本框来添加新的预订详细信息。

我的想法是执行以下操作,但由于 Booking 为空,我收到了 System.NullReferenceException

@page
@model Onlinebooking.Web.Pages.BookingModel
@
    ViewData["Title"] = "Booking";


<h1>Booking4</h1>
<form method="post">
    Reference: <Input asp-for="Booking.Reference" /><br />
    Pieces: <input asp-for="Booking.BookingDetails[0].Pieces" /><br />
    Marks: <input asp-for="Booking.BookingDetails[0].Marks" /><br />
    <br />
    <input type="submit" value="submit" />
</form>

<form method="post" asp-page-handler="DeleteBookingDetails">
    @for (var i = 0; i < @Model.Booking.BookingDetails.Count(); i++)
    
        @html.LabelFor(x => x.Booking.BookingDetails[i].Pieces)
        @Html.LabelFor(x => x.Booking.BookingDetails[i].Marks)
    
</form>

是否有可能在一个页面上完成所有这些操作,还是应该换个方向?

感谢您的帮助!

【问题讨论】:

这是可能的;您甚至可以使用模板来定义正确的EditorFor ...顺便说一句,为什么@Model.Booking.BookingDetails.Count()-1 中的-1 这可能很难,因为您正试图将数据模型用作视图模型。它们确实应该是不同的东西(可能具有许多相同的属性)。您在 UI/View 上看到的所有内容都应该在 ViewModel 中具有一个属性,但并非所有这些属性都会最终出现在您的数据库中。 @Stefan 我看到-1 是一个错误,没有任何意义。我编辑了帖子。 【参考方案1】:

这是一个添加和删除 BookingDetai 的演示:

cshtml:

<form method="post">
    <Input asp-for="Booking.Id" hidden />
    Reference: <Input asp-for="Booking.Reference" /><br />
    Pieces: <input name="Booking.BookingDetails[@Model.Booking.BookingDetails.Count()].Pieces" /><br />
    Marks: <input name="Booking.BookingDetails[@Model.Booking.BookingDetails.Count()].Marks" /><br />
    <br />
    <input type="submit" value="submit" asp-page-handler="AddBookingDetails"/>
    <div hidden>
        @for (var i = 0; i < @Model.Booking.BookingDetails.Count(); i++)
        
            <input asp-for="@Model.Booking.BookingDetails[i].Id" hidden />

            <input asp-for="@Model.Booking.BookingDetails[i].Pieces" hidden />

            <input asp-for="@Model.Booking.BookingDetails[i].Marks" hidden />


        
    </div>
   
</form>

    <table>
        <thead>
            <tr>
                <th><label class="control-label">Pieces</label></th>
                <th><label class="control-label">Marks</label></th>
                <th><label class="control-label">Action</label></th>
            </tr>
        </thead>
        <tbody>
            @for (var i = 0; i < @Model.Booking.BookingDetails.Count(); i++)
            
            <tr>
                <form method="post" >
                    <div hidden>
                        @for (var j = 0; j < @Model.Booking.BookingDetails.Count(); j++)
                        
                            <input asp-for="@Model.Booking.BookingDetails[j].Id" hidden />

                            <input asp-for="@Model.Booking.BookingDetails[j].Pieces" hidden />

                            <input asp-for="@Model.Booking.BookingDetails[j].Marks" hidden />


                        
                    </div>
                    <td>@Model.Booking.BookingDetails[i].Pieces</td>
                    <input name="Booking.Id" hidden value="@Model.Booking.Id" />
                    <input name="Booking.Reference" hidden value="@Model.Booking.Reference" />
                    <input name="BookingDetail.Id" hidden value="@Model.Booking.BookingDetails[i].Id" />
                    <td>@Model.Booking.BookingDetails[i].Marks</td>
                    <td><button asp-page-handler="DeleteBookingDetails">delete</button></td>
                </form>
            </tr>
            
        </tbody>
    </table>

cshtml.cs(我用简单的数据来测试一下):

   public class CreateBookingModel : PageModel
    
        [BindProperty]
        public Booking Booking  get; set; 
        [BindProperty]
        //BookingDetail is used to delete BookingDetail 
        public BookingDetail BookingDetail  get; set; 
        public static int count;
        public ActionResult OnGet()
        
            List<BookingDetail> list = new List<BookingDetail>  new BookingDetail  Id = 1, Pieces = 2, Marks = "m1" , new BookingDetail  Id = 2, Pieces = 1, Marks = "m2"  ;
            Booking = new Booking  Id = 1, Reference = "r", BookingDetails = list ;
            count = 2;
            return Page();
        
        public ActionResult OnPostAddBookingDetails() 

            Booking.BookingDetails[Booking.BookingDetails.Count()-1].Id = ++count;

            return Page();
        
        public ActionResult OnPostDeleteBookingDetails() 
            Booking.BookingDetails.RemoveAll(b => b.Id == BookingDetail.Id);
            return Page();
        
    

结果:

【讨论】:

感谢您的评论!我学到了很多。但是我在删除列表的一行时遇到了问题。当我通过在OnPostDeleteBookingDetails 中执行Booking.BookingDetails.RemoveAt(1); 删除时,我看到了奇怪的行为。您能否将删除功能添加到您的示例中? 我已经更新了绑定 Booking.Id 和您要添加或删除的 BookingDetail 的答案 我认为您的代码现在已损坏。添加和删​​除不起作用。我很抱歉。我无法弄清楚如何让它工作...... 你可以得到Booking Id和你要添加或删除的BookingDetail,然后你可以使用Booking Id在数据库中找到Booking,并添加或删除它的BookingDetail。我只是写了一个演示到模型绑定所需的数据,而无需编写从数据库中添加或删除数据。 知道了。我真的很喜欢您的代码将数据添加到模型而不将其写入数据库,因为我不需要它,因为我想将数据发布到 webapi。无论如何:在您修改代码后,添加数据不起作用,也不能删除。而且我不知道如何解决它。我们非常接近有一个非常好的解决方案,可以完全满足我的需求。那么有没有机会从您的第一个代码版本中删除添加的数据中的数据?我想一个问题是,当数据“仅”写入模型时,还没有设置 ID...【参考方案2】:

在您的问题中,我也遇到了很多困难。

您遇到的第一个问题是在 CSHTML.CS 文件中。初始化变量将解决这个问题。

public class BookingModel : PageModel

    private readonly DataDbContext _context;

    public BookingModel(DataDbContext context)
    
        _context = context;
    

    public List<Booking> Bookings  get; set;  = new List<Booking>();
    public Booking Booking  get; set;  = new Booking();
    public BookingDetail BookingDetail  get; set;  = new BookingDetail();

    public void OnGet()
    
        Bookings = _context.Bookings.Include(x => x.BookingDetails).ToList();
    

在 CSHTML 方面,我会稍微修改您引用标签和打印 FOR 循环的方式。

<form method="post" asp-page-handler="DeleteBookingDetails">
<table>
    <thead>
        <tr>
            <th><label asp-for="BookingDetail.Pieces" class="control-label"></label></th>
            <th><label asp-for="BookingDetail.Marks" class="control-label"></label></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var booking in Model.Bookings)
        
            foreach (var bookingDetail in booking.BookingDetails)
            
                <tr>
                    <td>@bookingDetail.Pieces</td>
                    <td>@bookingDetail.Marks</td>
                </tr>
            
        
    </tbody>
</table>

我没有修改 Booking 或 BookingDetail。

我相信您将能够从这里找出其余的功能。

【讨论】:

以上是关于Asp .Net Core 3.1 Razor 页面:复杂模型绑定的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET Core 3.1 Razor 将字符串转换为日期时间

ASP.NET Core 3.1 Razor 页面路由不明确匹配异常

Asp.net Core 3.1-如何从控制器返回到 Razor 页面?

在 ASP.net core 3.1 Razor Pages 中打印 PDF

创建基于ASP.NET core 3.1 的RazorPagesMovie项目-应用模型类配合基架生成工具生成Razor页面

基于动作的重定向 ASP.Net Core Razor