【问题标题】:Filter data received from database to controller into view过滤从数据库接收到控制器的数据到视图中
【发布时间】:2021-09-21 04:16:32
【问题描述】:

我在 SQL Server 中有一个学生详细信息表,如下所示:

我正在使用 MVC 框架来获取数据并在视图中以某种方式显示它。

这是我的模型代码:

// Students.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Vegam.Models
{
    public class Students
    {
        public int studentId { get; set; }
        public string studentName { get; set; }
        public string subject { get; set; }
        public int marks { get; set; }
        public List<Students> studentInfo { get; set; }
    }
}

我在控制器中创建了一个对象来访问数据:

// StudentController.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Vegam.Models;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

namespace Vegam.Controllers
{
    public class StudentController : Controller
    {
        // GET: Student
        public ActionResult Index(Students students)
        {
            string connection = ConfigurationManager.ConnectionStrings["StudentsConnection"].ConnectionString;
            SqlConnection sqlConnection = new SqlConnection(connection);
            string query = "select * from [dbo].[Student]";
            SqlCommand sqlCommand = new SqlCommand(query);
            sqlCommand.Connection = sqlConnection;
            sqlConnection.Open();
            SqlDataReader sdr = sqlCommand.ExecuteReader();
            List<Students> studentsModel = new List<Students>();
            if(sdr.HasRows)
            {
                while(sdr.Read())
                {
                    var studentDetails = new Students();
                    studentDetails.studentId = (int) sdr["FStudentID"];
                    studentDetails.studentName = sdr["FStudentName"].ToString();
                    studentDetails.subject = sdr["FSubject"].ToString();
                    studentDetails.marks = (int) sdr["FMarks"];
                    studentsModel.Add(studentDetails);
                }
                students.studentInfo = studentsModel;
                sqlConnection.Close();
            }
            return View("Index", students);
        }
    }
}

在我看来,我想以这种表格格式返回数据:

到目前为止,我已经想出了这个代码:

// Index.cshtml

@model Vegam.Models.Students

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <center>
        <h1>Answer 1</h1>
        @if(Model != null)
        {
            if(Model.studentInfo.Count > 0)
            {
                <table>
                    <tr>
                        <th>Student Name</th>
                        <th>EC1</th>
                        <th>EC2</th>
                        <th>EC3</th>
                        <th>EC4</th>
                        <th>EC5</th>
                        <th>Total</th>
                    </tr>
                    @foreach(var item in Model.studentInfo)
                    {
                        <tr>
                            <td>@Html.DisplayFor(m => item.studentName)</td>
                        </tr>
                    }
                </table>
            }
        }
    </center>
</body>
</html>

我不知道如何编写其余的&lt;td&gt; 元素。无论我写什么,都会返回所有学生姓名,包括重复的姓名。我应该在视图中过滤还是在控制器中创建单独的对象?我不能使用 SQL 函数或数据表来过滤数据,只能使用自定义对象。

更新
我接受了https://stackoverflow.com/users/1410664/caius-jard 的建议,这是我的新控制器代码和视图代码:-

public ActionResult Index(Students students)
        {
            using (var c = new SqlConnection(ConfigurationManager.ConnectionStrings["StudentsConnection"].ConnectionString)) {
                var ss = c.Query<Students>("select * from student");
                var d = new Dictionary<int, StudentViewModel>();
                foreach (var s in ss)
                {
                    StudentViewModel svm;
                    if (!d.TryGetValue(s.FStudentID, out svm)){
                        d[s.FStudentID] = svm = new StudentViewModel();
                        svm.Name = s.FStudentName;
                    }
                    if (s.FSubject == "EC1")
                        svm.EC1 = s.FMarks;
                    else if (s.FSubject == "EC2")
                        svm.EC2 = s.FMarks;
                    else if (s.FSubject == "EC3")
                        svm.EC3 = s.FMarks;
                    else if (s.FSubject == "EC4")
                        svm.EC4 = s.FMarks;
                    else if (s.FSubject == "EC5")
                        svm.EC5 = s.FMarks;
                }
                return View("Index", d.Values);
            }
        }
@model Vegam.Models.StudentViewModel

    @{
        Layout = null;
    }

    <!DOCTYPE html>

    <html>
    <head>
        <meta name="viewport" content="width=device-width" />
        <title>Index</title>
    </head>
    <body>
        <center>
            <h1>Answer 1</h1>
            @if (Model != null)
            {
                <table>
                    <tr>
                        <th>Student Name</th>
                        <th>EC1</th>
                        <th>EC2</th>
                        <th>EC3</th>
                        <th>EC4</th>
                        <th>EC5</th>
                        <th>Total</th>
                    </tr>
                    <tr>
                        <td>@Model.Name</td>
                    </tr>
                </table>
            }
        </center>
    </body>
    </html>

【问题讨论】:

  • 根据问题指南,请不要发布代码、数据、错误消息等的图像 - 将文本复制或输入到问题中。请保留将图像用于图表或演示渲染错误,无法通过文本准确描述的事情。
  • 我不能使用 SQL 函数 - 如果你要做的只是select * 大量数据它并在 c# 中处理它。数据库在处理数据方面比 c# 好得多,并且要执行客户端数据透视,正如您所发现的,必须将相对大量的数据从 db 传输到 c#。你的基本问题是你需要做一个支点,而你没有。您可以在数据库中执行此操作并节省大量资源并使您的 c# 生活更轻松。您可以在 c# 控制器中执行此操作并保持视图简单,或者您可以在视图中执行此操作..
  • ps;对公共属性使用 PascalCase,而不是 camelCase。不要给类提供复数名称。如果一个类代表一个集合,则在它后面加上 Collection。如果一个属性返回一个集合,给它一个复数名称
  • 对于一个学生实例也有一个学生列表在这种情况下并没有什么意义;它似乎仅存在,因此您可以将学生列表返回到视图,并且您让运行时通过将其作为 get 的参数来创建持有学生,这有点奇怪。只需让您的索引创建一个 List 并返回它,从学生类中删除 List 属性

标签: c# sql-server asp.net-mvc


【解决方案1】:

在数据库中完成,您的数据透视表如下所示:

SELECT FStudentName, ec1, ec2, ec3, ec4, ec5, ec1+ec2+ec3+ec4+ec5 as Tot, 
FROM student
PIVOT(
    SUM(FMarks)
    FOR FSubject IN (ec1, ec2, ec3, ec4, ec5)
) x

在 C# 中完成,它可能看起来像:

using var c = new SqlConnection("conn str here");
//use Dapper; life is too short to spend it writing mind numbing
//while reader.read someobject.someproperty = reader.getxxx("somecolumn")
//adjust your Students class so it is called Student
//and has properties whose names match the db columns
var ss = c.Query<Student>("select * from student"); 

var d = new Dictionary<int, StudentViewModel>();
foreach(var s in ss){
  StudentViewModel svm;
  if(!d.TryGetValue(s.FStudentID, out svm){
    d[s.FStudentID] = svm = new StudentViewModel();
    svm.Name = s.FStudentName;
  }
  if(s.FSubject == "EC1")
    svm.EC1 = s.FMarks;
  else if(s.FSubject == "EC2")
    svm.EC2 = s.FMarks;
  else if(s.FSubject == "EC3")
    svm.EC3 = s.FMarks;
  else if(s.FSubject == "EC4")
    svm.EC4 = s.FMarks;
  else if(s.FSubject == "EC5")
    svm.EC5 = s.FMarks;
}

return View("Index", d.Values);

您需要一个 StudentViewModel 类,该类具有名称和 EC1 到 EC5 的 5 个属性。大多数情况下,Visual Studio 会从您那里编写;只需指向每条摆动线并接受相关建议,如“生成类 StudentViewModel”、“添加成员..创建属性 EC1”等。它将从它所知道的关于 Student 类的信息中获取所有类型。 Student 类是数据库方面的东西;您的视图不应该使用它。学生的属性应该映射到 db 列名,这让生活变得更轻松;安装 Dapper(右键单击项目引用,管理 nuget 包,添加 Dapper。Dapper 执行数据库的所有查询,读取结果并为您填充 Student 实例

我知道你说你想在 c# 中做数据透视,但我把 db 版本放在那里主要是为了强调在 db 端做它是多么容易..它也会导致更少的数据通过网络从数据库拉到服务器。当您编写繁忙的应用程序时,您必须尽可能地进行所有优化。还有很多其他巧妙的方法来透视数据,但主要是我的目标是远离 LINQ 和过于复杂的数据结构,并提供一些你可以遵循的东西。枢轴的主要思想是您有一个重复值列表(学生ID)。第一次遇到它时它不在您的字典中,因此 TryGetValue 返回 false。这是创建跟踪属性的 StudentViewModel 并将其添加到字典中的提示,以便下次我们将新数据(不同的标记/主题)放入已创建的实例时看到相同的内容。这样,数据的顺序无关紧要;在结果集结束时,您将拥有一个唯一学生 ID 的字典,并且每个 StudentViewModel 最多填写 5 个属性。您来自 db 的 10 行结果集已成为具有 5 个属性的 2 个对象。您的 View 所要做的就是放置 td 5 个尖头,每个标记一个,然后您分别访问每个不同的 ECx

C# 可以做的一件事在数据库中并不那么容易;数据透视表中的动态列数。为此,您会将标记存储在字典中,将主题名称映射到标记。您将创建所有主题名称的超集,然后在为每个主题布置视图时 TryGetValue 并标记是否存在。它要复杂得多。在开始进行动态支点之前,也许可以先了解这里提供的简单版本

【讨论】:

  • 所以,我安装了 Dapper,制作了一个 StudentViewModel(带有对象),更新了 Student 类、控制器和 Index.cshtml。但是现在我面临一个运行时错误,即找不到资源。我在控制器中做错了吗?在问题中发布了新的控制器代码。
  • 您的 Index() 方法不需要参数。您是否已更新视图以使用将从控制器获取的 StudentViewModel 的 IEnumerable?
  • 我已从 Index 方法中删除了参数,但我不确定您所说的更新视图的内容。我已经在问题中发布了更新的视图。当我现在运行应用程序时,出现运行时错误:- 传递到字典中的模型项的类型为“System.Collections.Generic.Dictionary`2+ValueCollection[System.Int32,Vegam.Models.StudentViewModel]”,但是此词典需要“Vegam.Models.StudentViewModel”类型的模型项。
  • 在视图中执行@model IEnumerable&lt;Vegam.Models.StudentViewModel&gt; - Index 方法返回的是 StudentViewModel 的集合,而不仅仅是一个。因此,您还需要一个循环来遍历模型并依次处理每个模型(&lt;td&gt; 所在的视图部分应该类似于@foreach(var svm in Model) ... &lt;td&gt;svm.EC1&lt;/td&gt; ...
  • 非常感谢!我自己也做了一些更改,例如将 StudentViewModel 属性的数据类型从 Object 更改为 Int。帮助计算控制器中的总数。
猜你喜欢
  • 1970-01-01
  • 2017-12-02
  • 1970-01-01
  • 2016-03-26
  • 1970-01-01
  • 1970-01-01
  • 2018-02-08
  • 1970-01-01
  • 2021-04-16
相关资源
最近更新 更多