【问题标题】:.Net Core Razor page with multiple drop-down's for the same entity.Net Core Razor 页面,同一实体有多个下拉列表
【发布时间】:2020-02-22 11:30:36
【问题描述】:

有什么方法可以让多个下拉菜单在同一个剃须刀页面上显示来自同一个数据库实体的数据,而无需创建多个具有新 ID 的类(例如 ResourceID;ResourceID1;ResourceID2)

我可以在“Create.cshtml”和“Edit.cshtml”剃须刀页面中显示带有来自 MS SQL 数据库的适当数据的下拉列表,但保存时选择的数据会显示所选资源的 ID 'Index.cshtml'、'Detail.cshtml' 和 'Delete.cshtml' 视图中资源的名称。

资源模型:

namespace ProjectReporting.Models
{
    public class Resource
    {
        public int ID { get; set; }

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

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

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

        [DefaultValue(true)]
        [Display(Name = "Active")]
        public bool IsActive { get; set; } = true;

        [Display(Name = "Is Manager")]
        public bool IsManager { get; set; }

        [Display(Name = "Is Forecast Owner")]
        public bool IsForecastOwner { get; set; }

        public ICollection<Project> Projects { get; set; }

    }
}

项目模型:

namespace ProjectReporting.Models
{
    public class Project
    {
        public int ID { get; set; }

        [Display(Name = "ID")]
        public int PID { get; set; }

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

        [Display(Name = "Forecast Owner")]
        public int ResourceID { get; set; }
        public Resource Resource { get; set; }

        [Display(Name = "DSM")]
        public int? ResourceID1 { get; set; }

        public ICollection<ProjectComment> ProjectComments { get; set; }

    }
}

Create.cshtml 页面:

@page
@model ProjectReporting.Pages.Projects.CreateModel

@{
    ViewData["Title"] = "Create";
}

<h1>Create</h1>

<h4>Project</h4>
<hr />
<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="Project.PID" class="control-label"></label>
                <input asp-for="Project.PID" class="form-control" />
                <span asp-validation-for="Project.PID" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Project.ProjectName" class="control-label"></label>
                <input asp-for="Project.ProjectName" class="form-control" />
                <span asp-validation-for="Project.ProjectName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Project.ResourceID" class="control-label"></label>
                <select asp-for="Project.ResourceID" class="form-control" asp-items="ViewBag.ResourceID"><option value="" default="" selected="">-- Select --</option></select>
            </div>
            <div class="form-group">
                <label asp-for="Project.ResourceID1" class="control-label"></label>
                <select asp-for="Project.ResourceID1" class="form-control" asp-items="ViewBag.ResourceID1"><option value="" default="" selected="">-- Select --</option></select>
            </div>
            <div class="form-group form-check">
                <label class="form-check-label">
                    <input class="form-check-input" asp-for="Project.IsArchived" /> @Html.DisplayNameFor(model => model.Project.IsArchived)
                </label>
            </div>
            <div class="form-group">
                <input type="submit" value="Create" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

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

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Create.cshtml 页面:

namespace ProjectReporting.Pages.Projects
{
    public class CreateModel : PageModel
    {
        private readonly ProjectReporting.Data.ApplicationDbContext _context;

        public CreateModel(ProjectReporting.Data.ApplicationDbContext context)
        {
            _context = context;
        }

        public IActionResult OnGet()
        {
            ViewData["OrganisationID"] = new             SelectList(_context.ProjectType.Where(a => a.IsActive == true), "ID", "TypeName");
            ViewData["ResourceID"] = new SelectList(_context.Resource.Where(a => a.IsActive & a.IsForecastOwner == true), "ID", "LongName");
            ViewData["ResourceID1"] = new SelectList(_context.Resource.Where(a => a.IsActive == true), "ID", "LongName");
            return Page();
        }

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

        // To protect from overposting attacks, please enable the specific properties you want to bind to, for
        // more details see https://aka.ms/RazorPagesCRUD.
        public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            _context.Project.Add(Project);
            await _context.SaveChangesAsync();

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

Index.cshtml 页面:

@page
@model ProjectReporting.Pages.Projects.IndexModel

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-page="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Project[0].PID)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Project[0].Organisation)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Project[0].ProjectName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Project[0].Resource)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Project[0].ResourceID1)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Project[0].IsArchived)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model.Project) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.PID)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Organisation.OrgName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ProjectName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Resource.LongName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => c)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.IsArchived)
            </td>
            <td>
                <a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
                <a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
                <a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

item.Resource.LongName 适用于第一个资源,但我希望 item.Resource.LongName 也能如此。

Index.cshtml.cs

namespace ProjectReporting.Pages.Projects
{
    public class IndexModel : PageModel
    {
        private readonly ProjectReporting.Data.ApplicationDbContext _context;

        public IndexModel(ProjectReporting.Data.ApplicationDbContext context)
        {
            _context = context;
        }

        public IList<Project> Project { get;set; }

        public async Task OnGetAsync()
        {
            Project = await _context.Project
                .Include(p => p.Resource).ToListAsync();
        }
    }
}

我已在迁移文件中设置 FK 以创建能够检索数据的数据库,并希望避免必须按资源创建一个类文件。

table.ForeignKey(
     name: "FK_Project_Resource_ResourceID",
     column: x => x.ResourceID,
     principalTable: "Resource",
     principalColumn: "ID",
     onDelete: ReferentialAction.Restrict);
table.ForeignKey(
     name: "FK_Project_Resource_ResourceID1",
     column: x => x.ResourceID1,
     principalTable: "Resource",
     principalColumn: "ID",
     onDelete: ReferentialAction.Restrict);

结果会在下拉列表中显示正确的数据,并在保存时显示正确的资源 ID。但是,索引、详细信息和删除页面仅显示 ResourceID 而不是 LongName。如果我将 Resource.LongName 用于第二个 ResourceID1,它会正确显示与 ResourceID 相同的 LongName。

如何在页面上有多个指向同一实体的资源下拉列表并在索引、详细信息和删除页面上显示 LongName?

【问题讨论】:

  • 您不能在同一实体上为不同的资源添加两个外键。此外,您的资源和项目之间的关系是一对多还是多对多?
  • 你好 Xing,我已经添加了这些外键,你可以从最后一段代码中看到。这种关系是多对多的,因为我可以将多个项目分配给同一资源(例如,作为预测所有者和 DSM),并且这些项目有多个资源。我是否缺少其中一张表中的外键?
  • 多对多没有像你这样配置,参考here
  • 我使用链接下提供的数据创建了一个具有 3 个模型的新项目:Project、Resource、ProjectResource。我使用 Project 而不是 Post 和 Resource 而不是 Tag 和 ProjectResource 而不是 PostTag。我收到上面提到的错误。

标签: asp.net-core drop-down-menu razor-pages viewbag displayfor


【解决方案1】:

你不清楚ResourceProject之间的关系,你不能同时使用ResourceIDResourceID1作为Resource属性来恢复不同的资源。

对于多对多关系,可以参考

https://docs.microsoft.com/en-us/ef/core/modeling/relationships#many-to-many

一种解决方法是您只需在处理程序中获取所有资源并在视图中检索它:

索引.cshtml.cs:

public class IndexModel : PageModel
{
    private readonly ProjectReporting.Data.ApplicationDbContext _context;

    public IndexModel(ProjectReporting.Data.ApplicationDbContext context)
    {
        _context = context;
    }

    public IList<Project> Project { get;set; }
    public IList<Resource> Resources { get; set; }

    public async Task OnGetAsync()
    {
        Resources = await _context.Resource.ToListAsync();
        Project = await _context.Projects
            .Include(p => p.Resource).ToListAsync();
    }
}

Index.cshtml:

<td>
    @Html.DisplayFor(modelItem => item.Resource.LongName)
</td>
<td>
    @{ 
        var resource = Model.Resources.FirstOrDefault(r => r.ID == item.ResourceID1);
    }
    @resource.LongName
</td>

【讨论】:

  • 你好 Xing,我对编码很陌生,所以对于新手错误或误解深表歉意。我试图复制 Microsoft 提供的多对多场景,但出现以下错误:严重性代码描述项目文件行抑制状态错误 CS1061“资源”不包含“ProjectResource”的定义,并且没有可访问的扩展方法“ProjectResource” ' 可以找到接受“资源”类型的第一个参数(您是否缺少 using 指令或程序集引用?) ManyToMany C:\VisualStudio2019Data\ManyToMany\Data\ManyToManyContext.cs 31 Active
  • 我使用“项目”而不是“帖子”和“资源”而不是“标签”
  • 我已经测试了你的解决方法,它适用于我想做的事情。我将进一步探讨多对多关系问题。谢谢。
猜你喜欢
  • 1970-01-01
  • 2016-04-01
  • 2021-12-05
  • 1970-01-01
  • 2020-06-16
  • 1970-01-01
  • 2020-01-24
  • 2019-04-09
  • 2022-01-20
相关资源
最近更新 更多