简体   繁体   English

C#聚合来自多个列的数据,这些列来自使用Entity Framework6的RAW SQL查询的结果集中,并将这些数据添加到ViewModel中的列表中

[英]C# aggregate data from mulitple columns taken from a resultset of an RAW SQL query with Entity Framework6 and add those data to Lists in a ViewModel

I want to process the data from my database-query using raw SQL in Entity Framework 6 as follows and need a best practice by the use of native functions of C# and LINQ: 我想按以下方式使用Entity Framework 6中的原始SQL处理来自数据库查询的数据,并需要通过使用C#和LINQ的本机功能来进行最佳实践:

PICTURE 1: Resultset taken from database 图1:结果集取自数据库

I have created a class for the resultset above, it looks like that: 我为上面的结果集创建了一个类,它看起来像这样:

public class ProjectQueryModel {

        public int Project { get; set; }
        public string Projectname { get; set; }

        public int RoomId { get; set; }
        public string RoomName { get; set; }

        public int? EmployeeId { get; set; }
        public string EmployeeName { get; set; }

        public int? QualificationId { get; set; }
        public string QualificationName { get; set; }
        public int? QualificationLevel { get; set; }
}

To this point the query works and I got all my data from it stored in a List of type ProjectQueryModel . 至此查询工作正常,我将所有数据存储在ProjectQueryModel类型List中 Now I want to add this data to my ViewModel and don't know how to use the functions C# offers me to process the data of resultsets. 现在,我想将此数据添加到ViewModel中,但不知道如何使用C#提供的功能来处理结果集的数据。 How can I achieve the following by saving every entity of type ProjectViewModel in a List, whose objects have the following structure: 如何通过将ProjectViewModel类型的每个实体保存在List中来实现以下目的,该对象的对象具有以下结构:

PICTURE 2: data organisation in ViewModel 图2:ViewModel中的数据组织

An example dataset for project 1 in the target list should look like this: 目标列表中项目1的示例数据集应如下所示:

ProjectId = 1
Projectname =   T1
RoomId =    1
RoomName =  Delta Room
======================
Employees *(Attribute of type List <ProjectEmployeesVM> )*
  [0].EmployeeId = 2
  [0].EmployeeName = Mee
  [0].EmployeeQualifications *(Attribute of type List<EmployeeQualificationsVM)*
      [0].EmployeeQualifications[0].QualificationId = 1
      [0].EmployeeQualifications[0].QualificationName = Programmer
      [0].EmployeeQualifications[0].QualificationLevel = 3
      ...any other qualification of the employee
  [1].EmployeeId = 2
  [1].EmployeeName = Mee
  [1].EmployeeQualifications
      [1].EmployeeQualifications[0]
  ...Any other employee in this project and all of his qualifications

What I also want to achieve is to save a empty list in case the project has no employees , because the resultset is achieved by the use of LEFT OUTER JOINS. 我还想实现的是在项目没有员工的情况下保存一个空列表 ,因为结果集是通过使用LEFT OUTER JOINS实现的。 For the qualifications it is not necessary, because every employee has at least one qualification. 对于资格没有必要,因为每个员工至少具有一个资格。

VERY BIG THANKS in advance 提前非常感谢

I'm supposing you have a constructor in every class involved that takes all the properties as arguments. 我假设您在每个涉及的类中都有一个构造函数,该构造函数将所有属性用作参数。

Here's how i would do it: 这是我的方法:

List<ProjectQueryModel> queryResult = ...;

List<ProyectViewModel> views = queryResult
    // Take all the rows that belong to one proyect
    .GroupBy(m => m.Proyect)
    // Convert every group into a ProyectViewModel
    // First use Select to Map every Group into a new Proyect using a function that takes a group of rows and return a Proyect
    // Then we use Aggregate inside that mapping function to collapse the entire group of rows into a single ProyectViewModel
    // We'll need a contructor in ProyectViewModel that gives us a completly empty instance
    // Aggregate takes a starting point, and a function that takes that starting point, and passes it every element of the IEnumerable we're using. The return value of that function is the "new starting point".
    // Using this we'll build the Proyect from every row.
    .Select(g => g.Aggregate(new ProyectViewModel(), (pvm, nxtRow) => {
        // Check if we haven't initialized the instance, and do so.
        if (pvm.ProyectId == null) pvm.ProyectId = nxtRow.Proyect;
        if (pvm.ProyectName == null) pvm.ProyectName = nxtRow.ProyectName;
        if (pvm.RoomId == null) pvm.RoomId = nxtRow.RoomId;
        if (pvm.RoomName == null) pvm.RoomName = nxtRow.RoomName;
        if (pvm.Employees == null) pvm.Employees = new List<ProyectEmployeeViewModel>();

        // If the row has an employee
        if (nxtRow.EmployeeId.HasValue) {
            // If the Employee is not yet on the Proyect add it
            if (!pvm.Employees.Any(e => e.EmployeeId == nxtRow.EmployeeId))                                
            {
                // This constructor should create the empty List of Qualifications
                pvm.Employees.Add(new ProyectEmployeeViewModel(nxtRow.EmployeeId.Value, nxtRow.EmployeeName);
            }

            // If the row has a qualification
            if (nxtRow.QualificationId.HasValue)
            {
                // Find it's employee
                pvm.Employees.First(e => e.EmployeeId == nxtRow.EmployeeId)
                // Add the current row's qualification to the employee
                    .Qualifications.Add(new EmployeeQualificationsViewModel(nxtRow.QualificationId.Value, nxtRow.QualificationName, nxtRow.QualificationLevel.Value));
            }
        }

        // Return the Proyect with the changes we've made so we keep building it
        return pvm;
    })).ToList();

LINQ is quite a beauty isn't it? LINQ很漂亮,不是吗?

There might be errors, but use this as a starting point. 可能会有错误,但是以此为起点。

Start by making sure that your database has the right foreign key constraints between your tables, then update your model. 首先确保您的数据库在表之间具有正确的外键约束,然后更新模型。 This will automatically create the correct navigation properties. 这将自动创建正确的导航属性。 I've assumed they will be called Employees and Qualifications, but change as appropriate. 我假设它们将被称为“员工和资格”,但会适当地进行更改。

Then your query just becomes: 然后您的查询就变成:

var result=db.Projects
  .Include(p=>p.Employees)
  .Include(p=>p.Employees.Select(e=>e.Qualifications))
  .Where(p=>p.id==1)
  .AsEnumerable(); // or .ToList() if you prefer

Then just pass IEnumerable<Project> to your view (or just Project if your view will always only get 1 Project -- in that case, just end the query with .First() instead of .AsEnumerable() ) . 然后,只需通过IEnumerable<Project>你的看法(或只是Project ,如果你的观点总是只拿到1个项目-在这种情况下,刚刚结束与查询.First()代替.AsEnumerable() Unless of course you like creating ViewModels, but I'm guessing you don't and this isn't a project that needs the added complexity or abstractions. 除非您当然喜欢创建ViewModels,否则我猜您不是,并且这不是一个需要增加复杂性或抽象性的项目。

The above code assumes you have the following tables: 上面的代码假定您具有下表:

Project (int Id, varchar(50) Name, int RoomId)
Room (int Id, int Name)
Employee (int Id, varchar(50) Name)
Qualification (int Id,varchar(50) Name, int Level)

Cross Reference tables: 交叉参考表:

ProjectEmployees (int ProjectId, int EmployeeId)
EmployeeQualifications (int EmployeeId, int QualificationId)

Foreign Keys: 外键:

Project.RoomId -> Room.Id
ProjectEmployees.ProjectId -> Project.Id
ProjectEmployees.EmployeeId -> Employee.Id
EmployeeQualifications.EmployeeId -> Employee.Id
EmployeeQualifications.QualificationId -> Qualification.Id

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM