如何在单个剃刀页面上联接和显示来自两个模型的数据(无 mvc)

How to Join and Display Data from two models on a single razor page (no mvc)

提问人:MelB 提问时间:9/22/2022 最后编辑:MelB 更新时间:9/23/2022 访问量:225

问:

我有两个模型(Child 和 Shot)想要加入,以便我可以在单个剃须刀页面上显示信息。

这个小型历史视图应用程序基本上在一个模型中具有客户人口统计数据(模型名称为子),在另一个表中具有客户免疫接种(模型名称为Shot)。我有一个索引页面,用户将单击客户 ID 号和客户名称旁边的“查看免疫详细信息”详细信息按钮。这将把他们带到我希望显示联接数据的页面。

页面的上半部分将显示来自儿童模型的人口统计数据(ID、姓名、地址、出生日期等),页面的下半部分将显示该特定客户(来自 Shot 模型)的任何免疫信息表。(免疫日期、剂量、注射说明等)

这是 Child 和 Shot 之间的一对多关系。一个子记录可以有多个 Shot 记录。

在回顾如何让两个模型显示在一个页面上时,我只看到了不需要通过一个模型的结果过滤数据的地方。如上所述,我只需要在“详细信息”页面上显示与单个客户端相关的镜头,而不是“镜头”表中的所有镜头。

这是我到目前为止所拥有的代码:

首先,子模型:

using System.ComponentModel.DataAnnotations;

namespace HealthyShots.Models
{
    public class Child
    {
        [Key]
        [Display(Name ="Child ID")]
        public int ChildId { get; set; }
        public List<Shot> Shots { get; set; }
        [Display(Name ="Last Name")]
        public string? LastName { get; set; }
        [Display(Name = "First Name")]
        public string? FirstName { get; set; }
        [Display(Name = "MI")]
        public string? MiddleInitial { get; set; }
        [Display(Name = "Full Name")]
        public string FullName
        {
            get
            {
                return LastName + ", " + FirstName + " " + MiddleInitial;
            }
        }
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
        [Display(Name = "Date of Birth")]
        public DateTime? BirthDate { get; set; }
        public string? Addr1 { get; set; }
        public string? Addr2 { get; set; }
        public string? City { get; set; }
        public string? State { get; set; }
        public string? Zip { get; set; }
    }
}

然后,Shot 模型:

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace HealthyShots.Models
{
    public class Shot
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [Key]
        public int ShotId { get; set; }

        [Required]
        public int ChildId { get; set; }
        public Child Child { get; set; }

        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
        [Display(Name = "Immunization Date")]
        public DateTime? Date { get; set; }
        public string? Dose { get; set; }
        [Display(Name = "Shot Number")]
        public int? ShotNo { get; set; }
        [Display(Name ="Shot Description")]
        public string? ShotDesc { get; set; }
    }
}

然后,我对详细信息剃刀页面的.cs(到目前为止):

using HealthyShots.Data;
using HealthyShots.Models;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;

namespace HealthyShots.Pages.Details
{
    public class DetailsModel : PageModel
    {
        private readonly ApplicationDbContext _db;

        public Child Child { get; set; }
        public Shot Shot { get; set; }
        

        public DetailsModel(ApplicationDbContext db)
        {
            _db = db;
        }
        
        public void OnGet(int Id)
        {
            _db.Child
                .Include(child => child.Shots)
                .FirstOrDefault(child => child.ChildId == Id);
        }

    }
}

还有我的剃刀视图:

@page
@model HealthyShots.Pages.Details.DetailsModel

@{
    ViewData["Title"] = "Immunization Details";
}

<h2>Details</h2>

<div>
    <h4>Demographic Information</h4>
    <hr />

        <table class="table table-bordeless" style="width:100%">
            <tr>
                <td style="width: 10%">
                    <div class="mb-3">
                        <label asp-for="Child.ChildId"></label>
                        <input asp-for="Child.ChildId" disabled class="form-control"/>
                    </div>
                </td>
                <td style="width: 30%">
                    <div class="mb-3">
                        <label asp-for="Child.LastName"></label>
                        <input asp-for="Child.LastName" disabled class="form-control"/>
                    </div>
                </td>
                <td style="width: 30%">
                    <div class="mb-3">
                        <label asp-for="Child.FirstName"></label>
                        <input asp-for="Child.FirstName" class="form-control"/>
                    </div>
                </td>
                <td style="width: 5%">
                    <div class="mb-3">
                        <label asp-for="Child.MiddleInitial"></label>
                        <input asp-for="Child.MiddleInitial" disabled class="form-control"/>
                    </div>
                </td>
                <td style="width: 25%">
                    <div class="mb-3">
                        <label asp-for="Child.BirthDate"></label>
                        <input asp-for="Child.BirthDate" type="date" disabled class="form-control"/>
                    </div>
                </td>
            </tr>
        </table>
        <table class="table table-bordeless" style="width:100%">
            <tr>
                <td style="width: 25%">
                    <div class="mb-3">
                        <label asp-for="Child.Addr1"></label>
                        <input asp-for="Child.Addr1" disabled class="form-control"/>
                    </div>
                </td>
                <td style="width: 25%">
                    <div class="mb-3">
                        <label asp-for="Child.Addr2"></label>
                        <input asp-for="Child.Addr2" disabled class="form-control"/>
                    </div>
                </td>
                <td style="width: 25%">
                    <div class="mb-3">
                        <label asp-for="Child.City"></label>
                        <input asp-for="Child.City" disabled class="form-control"/>
                    </div>
                </td>
                <td style="width: 5%">
                    <div class="mb-3">
                        <label asp-for="Child.State"></label>
                        <input asp-for="Child.State" disabled class="form-control"/>
                    </div>
                </td>
                <td style="width: 20%">
                    <div class="mb-3">
                        <label asp-for="Child.Zip"></label>
                        <input asp-for="Child.Zip" disabled class="form-control"/>
                    </div>
                </td>
            </tr>
        </table>
<br />
<br />


    <h4>Immunizations</h4>
    <hr />

          <table class="table table-bordeless" style="width:100%">
            <tr>
                <td style="width: 20%">
                    <div class="mb-3">
                        <label asp-for="Shot.Date"></label>
                        <input asp-for="Shot.Date" type="date" disabled class="form-control"/>
                    </div>
                </td>
                <td style="width: 20%">
                    <div class="mb-3">
                        <label asp-for="Shot.Dose"></label>
                        <input asp-for="Shot.Dose" disabled class="form-control"/>
                    </div>
                </td>
                <td style="width: 60%">
                    <div class="mb-3">
                        <label asp-for="Shot.ShotDesc"></label>
                        <input asp-for="Shot.ShotDesc" disabled class="form-control"/>
                    </div>
                </td>
            </tr>
            }
        </table>

    <a asp-page="/Children/Index">Back to List</a>
</div>

我知道我缺少代码。我已经在这里搜索并搜索了外键的工作原理,但仍然不完全理解它。我意识到我必须告诉Visual Studio,Id(在 Child 模型中)必须与 ChildId(在 Shot 模型中)相同,但我不确定在哪里执行此操作或正确的语法是什么。

我还需要一些关于如何在剃须刀页面上引用这些项目的指导。一旦它们通过外键连接,我是否可以像引用单个模型一样使用 asp 标记帮助程序访问它们?就像我到目前为止在我的代码中所做的那样?

提前感谢您提供的任何指导。我是初学者,所以当你提出你的答案时,请理解这一点。

语法 foreign-keys inner-join razor-pages

评论


答:

1赞 Dimitris Maragkos 9/22/2022 #1

使用约定创建关系

在 EF Core 中,定义实体关系的一种方法是使用约定。这意味着必须将具有特定名称的特定属性添加到实体,以便 EF 了解它们之间的关系。

例:

public class Child
{
    public int ChildId { get; set; }

    public List<Shot> Shots { get; set; }
}

public class Shot
{
    public int ShotId { get; set; }

    public int ChildId { get; set; }
    public Child Child { get; set; }
}

在上述场景中,EF 将自动在 和 之间创建一对多关系。一个镜头与一个孩子相关联,但一个孩子可以链接到多个镜头。 将是 的外键。ShotChildShot.ChildIdChild.ChildId

https://www.learnentityframeworkcore.com/conventions/one-to-many-relationship

从数据库加载相关数据

现在,如果您想从数据库中检索包含所有相关镜头的特定子项,您可以执行以下操作:

var child = _db.Child
    .Include(child => child.Shots)
    .FirstOrDefault(child => child.ChildId == Id);

Include方法告诉 EF 填充与这个孩子相关的所有镜头。Child.Shots

同样,如果您在加载镜头时可以执行以下操作:

var shots = _db.Shot
    .Include(shot => shot.Child)
    .ToList();

https://learn.microsoft.com/en-us/ef/core/querying/related-data/eager

手动配置关系

您还可以手动配置关系:

https://www.learnentityframeworkcore.com/configuration/one-to-many-relationship-configuration

评论

0赞 MelB 9/23/2022
你能告诉我我应该在我的代码中的哪个位置放置_db吗?孩子。Include(child => 子项。镜头)。FirstOrDefault(child =>子项。ChildId == Id);
0赞 MelB 9/23/2022
我假设它会进入公共无效 OnGet(int Id) ?我的观点仍然没有拉入数据。不知道有什么关闭。
0赞 Dimitris Maragkos 9/23/2022
是的,它应该进入方法。在该方法中放置一个断点并检查发生了什么。method 的参数有值吗?数据库中是否存储了具有此 ID 的任何 ID?还有为什么是和属性?OnGetIdOnGetChildChildShotvirtual
0赞 MelB 9/23/2022
虚拟是试图弄清楚外键的东西。
0赞 Dimitris Maragkos 9/23/2022
它们不应该是虚拟的。您是否尝试在方法中放置断点并查看得到什么?OnGet