MVC dbContext find parent record when current record has no elements

ε祈祈猫儿з 提交于 2019-12-13 04:06:01

问题


I'm a MVC/C# beginner so go easy.

I have an existing database where a Customer has zero or many Projects. I've built an ASP.Net project from ADODB Entity data model and dbContext code generators.

I have a customer: Joe Bloggs (ID=7). I click on the 'Projects' link for Joe Bloggs in my form to see his projects. He doesn't have any. I want to create a project for him so I call the Create action on the project controller.

I need to pass Joe Bloggs ID to the Create action for three reasons (which may not be necessary - please enlighten me if need be)

  1. Because this is a project for Joe Bloggs I need to pass ID=7 into the controller so I can set the default Customer ID for him when generating the model data for the create view

  2. When I hit the Cancel button on the Create view, I want to go back to the originally filtered view of projects based on Joe Blogs

  3. When I hit save I want to got back to the originally filtered view of projects based on Joe Bloggs.

Anyway if I do have one or more projects for Joe Bloggs then I can use this monster below to access his id from the model data within the view:

<input type="button" title = "New Project" value="New Project" onclick="location.href='@Url.Action("Create", new { id = Model.First().Customer_ID })'" />

Here is the issue: if he doesn't have any projects, Model.First() doesn't return anything so I can't find the 'parent' Customer_ID record.

I used dbContext to generate the model classes (this is a data-first development). Something tells me I could extend these classes or create a new class to allow for the case above.

My current workaround is to use ViewBag to pass the various id's and strings from viewer > controller like a hot potato but this means if get three deep (Customer > Proejct > Task), and I want to display the customer name on the task, I've passed it twice. It smells.

I notice that the drop down in the Project Index view has the Customer in it. Here is my code for that:

@Html.DropDownList("Customer_ID", null, "Select a Customer to Filter", new { @onchange = "this.form.submit();" })

I might be able to hack it out of there but really I want to be able to traverse up from a class which might be two or three deep and far removed from this drop down

Here is an abridged Customer and CustomerProject class generated from dbContext

public partial class Customer
{
    public Customer()
    {
        this.Tasks = new HashSet<Task>();
        this.CustomerProjects = new HashSet<CustomerProject>();
    }

    public int Customer_ID { get; set; }
    public string Customer_Name { get; set; }

    public virtual ICollection<CustomerProject> CustomerProjects { get; set; }
}

public partial class CustomerProject
{
    public CustomerProject()
    {
        this.Tasks = new HashSet<Task>();
        this.CustomerProjectTasks = new HashSet<CustomerProjectTask>();
    }

    public int CustomerProject_ID { get; set; }
    public int Customer_ID { get; set; }
    public string Project_Name { get; set; }

    public virtual ICollection<Task> Tasks { get; set; }
    public virtual Customer Customer { get; set; }
    public virtual ICollection<CustomerProjectTask> CustomerProjectTasks { get; set; }
}

I'm sure there is an obvious solution but my strength is in databases and VB6, not C#

At Mystere Man's suggestion I have built a ViewModel class, but I'm having a little trouble:

Here is what's in my model class file (as I understand it this is just a wrapper around the the existing project entity):

namespace zzz.Models
{
    using System;
    using System.Collections.Generic;

    public class ProjectsViewModel
    {
        public int Customer_ID { get; set; }
        public ICollection<CustomerProject> CustomerProjects;
    }
}

Here's whats in the Index action of my controller (I havejust added my existing Project collection to the new ViewModel class):

    public ViewResult Index(int pCustomer_ID = 0, string pProjectName_Filter = "")
    {

        // Set up drop down
        ViewBag.Customer_ID = new SelectList(db.Customers.OrderBy(x => x.Customer_Name), "Customer_ID", "Customer_Name");
        //ViewBag.Selected_Customer_ID = Customer_ID;

        // If no parameters entered, show nothing
        // Otherwise optionally filter each parameter
        var projects = from p in db.CustomerProjects
            orderby p.Active, p.Project_Order
            where
            (p.Project_Name.Contains(pProjectName_Filter) || pProjectName_Filter.Equals("")) &&
            (p.Customer_ID == pCustomer_ID || pCustomer_ID.Equals(0)) &&
            !(pCustomer_ID.Equals(0) && pProjectName_Filter.Equals(""))
            select p;


        var customerprojects = new ProjectsViewModel
        {
            Customer_ID = pCustomer_ID,
            CustomerProjects = projects.ToList()
        };


        return View(customerprojects);
    }

Here's an extract from my view (I have tried to iterate through the Project collection within the ViewModel class):

@model IEnumerable<BistechPortal.Models.ProjectsViewModel>

<table>
@foreach (var item in Model.CustomerProjects)
{
<tr>
    <td>
        @Html.DisplayFor(modelItem => item.Project_Name)
    </td>
</tr>
}
</table>

When I run the Index action, on the foreach line I get:

'System.Collections.Generic.IEnumerable<zzzzPortal.Models.ProjectsViewModel>' does not contain a definition for 'CustomerProjects' "

Please translate - why can't it find my 'CustomerProjects' collection inside my ViewModel?


回答1:


This is why you should not pass entity objects directly to the view. In most cases, the view needs more information (or different information) than what the defined entity provides.

This is why View Models exist. You would create a View Model that contains the Customer_ID and the Projects (and any other data you need). Then, you can access the Customer_ID even if the user has no projects.

public class ProjectsViewModel {
     public int Customer_ID {get;set;}
     public List<CustomerProject> CustomerProjects;
}

Then you pass this new object to your view.

(added by the OP:)

To pass this object to your view you need to tell the view that it is no longer accepting data of type CustomerProjects, and (this took me three hours to work out) that it is no longer getting 'enumerable' data.

So in your view change

@model IEnumerable<Portal.Models.CustomerProjects>

to

@model Portal.Models.ProjectsViewModel

Also in your view you now want to iterate over Model.CustomerProject, so change

@foreach (var item in Model)

to

@foreach (var item in Model.CustomerProjects)


来源:https://stackoverflow.com/questions/12661411/mvc-dbcontext-find-parent-record-when-current-record-has-no-elements

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!