【问题标题】:Access Data From One Model In Another In .NET Core MVC在 .NET Core MVC 中从一个模型访问另一个模型的数据
【发布时间】:2019-12-03 06:00:54
【问题描述】:

我有一个 Employee 模型:

public class Employee
    {
        public int Id { get; set; }

        [Required]
        [StringLength(30, MinimumLength = 5, ErrorMessage = "Name must be between 5 and 30 characters.")]
        [RegularExpression(@"^[A-Z]+[a-zA-Z""'\s-]*$")]
        public string Name { get; set; }

        [Required]
        [Display(Name = "Direct Contact")]
        [RegularExpression(@"^([0-9]{10})$", ErrorMessage = "Invalid contact number. Must be 10 digits.")]
        public string Phone { get; set; }

        [Display(Name = "Personal Cell")]
        [RegularExpression(@"^([0-9]{10})$", ErrorMessage = "Invalid contact number. Must be 10 digits.")]
        public string Phone2 { get; set; }

        [RegularExpression(@"^([0-9]{10})$", ErrorMessage = "Invalid fax number. Must be 10 digits.")]
        public string Fax { get; set; }

        [Required]
        public string Email { get; set; }

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

可以在 /employees/ 的表中查看员工数据

然后我正在尝试创建一个项目模型(可以在 /projects/ 中查看)。基本上,在查看项目列表时,其表中的类别之一将是项目联系人。因此,我希望能够将其中一名员工姓名链接为项目联系人。

这是我的项目模型:

public class Project
    {
        public int Id { get; set; }

        [ForeignKey("Employee")]
        public int EmployeeId { get; set; }

        public string ProjectName { get; set; }

        [DataType(DataType.Date)]
        public DateTime DueDate { get; set; }

        public virtual Employee Employee { get; set; }
    }

我这样做的方式是否正确?我似乎无法弄清楚如何正确链接这两个表,以便我可以从项目视图中访问员工的姓名。

  • 编辑 -

我在想我拥有它的方式,当将数据加载到 Project 表中时,我可以将 EmployeeID 添加为 Employee 表中的任何员工 ID。然后我可以正确访问它吗?我这样想对吗?

【问题讨论】:

    标签: .net asp.net-mvc entity-framework asp.net-core model-view-controller


    【解决方案1】:

    我这样做的方式是否正确?我似乎无法弄清楚如何正确链接这两个表,以便我可以从项目视图中访问员工的姓名。

    你可以这样做:

    Employee employee =  _context.Projects.Include(p => p.Employee).FirstOrDefault(empId);
    

    现在您可以在视图中访问Employee's Name,如下所示:

    @Model.Employee.Name
    

    更多详情:Loading Related Data in EF Core

    【讨论】:

    • 那么走这条路,我不需要在我的项目模型中添加任何关于员工的东西吗?
    • 是的!您无需再做任何事情!
    • 谢谢。我会将它添加到我的 ProjectsController 中吗?我还会从我的模型中删除 EmployeeID 外键和虚拟 Employee 吗?
    • 是的!在将呈现项目视图的方法中!
    • 所以我使用以下字段创建了我的项目模型:Id、ProjectName 和 DueDate。所以现在我会在 index 方法中将你的代码行添加到 ProjectsController 中,对吧?我不太明白如何获得与某个项目相对应的特定员工。我不需要在我的一个模型中使用一个字段将两者联系在一起吗?
    【解决方案2】:

    在显示详细信息列表时,我建议采用视图模型类来仅表示您需要显示的数据以及识别它们所需的键。将 EF 实体作为模型传递给视图将导致性能和序列化问题,以及如果将实体从视图传递回控制器时的潜在漏洞。性能下降来自于必须从数据库中急切加载比您需要的更多的数据,或者在序列化程序触及引用时导致延迟加载调用。

    相反,如果您使用以下视图模型:

    [Serializable]
    public class ProjectViewModel
    {
        public int ProjectId { get; set; }
        public string ProjectName { get; set; }
        public DateTime DueDate { get; set; }
        public string EmployeeName { get; set; }
    }
    

    然后您的项目列表使用IEnumerable<ProjectViewModel> 作为它的模型(或包含IEnumerable<ProjectViewModel> 的包装器视图模型)。

    要从 EF 返回视图模型,您可以利用 Select 或 Automapper 的 ProjectTo<T> 方法。例如Select

    var projects = context.Projects.Where(x => /* criteria */)
        .Select(x => new ProjectViewModel
        {
            ProjectId = x.Id,
            ProjectName = x.ProjectName,
            DueDate = x.DueDate,
            EmployeeName = x.Employee.Name
        }).ToList();
    

    如果您使用 Automapper,那么约定会解决大部分问题,而您可以:

    var projects = context.Projects.Where(x => /* criteria */)
        .ProjectTo<ProjectViewModel>()
        .ToList();
    

    ...最多可能需要配置来处理“Id”。

    .Include 相比,这种方法的主要优点是生成的 SQL 效率更高,并且通过网络传输的数据更紧凑。

    使用 ViewModel,您的 SQL SELECT 语句大致如下:

    SELECT p.Id, p.ProjectName, p.DueDate, e.Name FROM Projects p INNER JOIN Employees e ON p.EmployeeId = e.Id /* WHERE criteria on p */;
    

    使用带有 Include 语句的实体会更像:

    SELECT p.*, e.* FROM Projects p INNER JOIN Employees e ON p.EmployeeId = e.Id /* WHERE criteria on p */
    

    后者从两个连接表中选择 所有 列。在像这样的简单示例中,它似乎并没有那么多,但是随着系统的增长以及表的 # 和大小的增加,它加起来非常快。如果只需要员工姓名,为什么要查询员工的所有列? 真正的风险是延迟加载是否开始涉及。在这种情况下,您会开始遇到以下情况:

    SELECT p.* FROM Projects p /* WHERE criteria on p */
    SELECT e.* from Employees e WHERE Id = 22
    SELECT e.* from Employees e WHERE Id = 19
    SELECT e.* from Employees e WHERE Id = 121
    SELECT e.* from Employees e WHERE Id = 104
    SELECT e.* from Employees e WHERE Id = 11
    // ...
    

    如果 Projects 查询返回 20 个项目,则 Project 上每个引用的实体最多有 20 个查询。 (无论您是否需要数据)延迟加载属性将在访问时查询上下文。当父实体被序列化时,序列化程序将访问每个属性,一次触发延迟加载 1 个父实体。 (许多订单的性能比使用Include 差)当您有循环引用时,这可能会导致序列化错误。 (A引用了B,B引用了A)

    作为一般规则,尽管许多 Microsoft 示例显示将实体发送到 MVC 视图,但不要将实体发送到视图;使用普通的 'ol C# 视图模型。

    【讨论】:

      【解决方案3】:

      在显示详细信息列表时,您需要使用 ViewModel(例如 ProjectDetailsViewModel)而不是使用 EF 实体,这将避免序列化问题。

      而不是使用.Include 来链接项目实体,如下所示:

      var employee =  _context.Projects.Include(p => p.Employee).FirstOrDefault(employeeId);
      

      使用如下所示的 ProjectDetailsViewModel:

      [Serializable]
      public class ProjectDetailsViewModel
      {
          public int ProjectId { get; set; }
          public string ProjectName { get; set; }
          public DateTime DueDate { get; set; }
          public string EmployeeName { get; set; }
          public string Phone { get; set; }
          public string Email{ get; set; }
      }
      

      改为进行投影(这将生成更优化的T-SQL 并解决序列化问题)

      var projects = context.Projects.Where(x => /* filter_criteria */)
          .Select(x => new ProjectDetailsViewModel
          {
              ProjectId = x.Id,
              ProjectName = x.ProjectName,
              DueDate = x.DueDate,
              EmployeeName = x.Employee.Name,
              Phone = x.Employee.Phone,
              Email = x.Employee.Email
          }).ToList();
      

      最后在你的 mvc 控制器中这样做:

      return View(projects);
      

      EF Documentation - How to create a Web API application that uses Entity Framework for database persistence.

      Increasing the Performance of Entity Framework with Projection Queries

      Read related data with EF in an ASP.NET MVC app

      【讨论】:

      • @burgoyne,这能解决您的问题吗?如果仍有问题,请告诉我。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-12
      • 1970-01-01
      • 2012-12-13
      • 2011-05-13
      • 2019-09-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多