【问题标题】:Creating objects from stored procedure result从存储过程结果创建对象
【发布时间】:2013-07-13 00:47:04
【问题描述】:

我们有一个带有以下查询的现有 SQL Server 存储过程。在下面的类设计中,我们需要根据查询结果创建一个Student 对象的集合。

使用LINQSqlDataReader 创建对象的最佳方法是什么?

注意:我只使用SqlDataReader;没有ORM

查询

SELECT 
    S.StudentID, S.StudentName, E.ExamID, E.ExamName, SE.Mark 
FROM 
    StudentExam SE
INNER JOIN 
    Student S ON S.StudentID = SE.StudentID
INNER JOIN 
    Exam E ON E.ExamID = SE.ExamID 

public class ExamMark
{
    public int ExamID { get; set; }
    public string ExamName { get; set; }
    public int Mark { get; set; }
}

public class Student
{
    public int StudentID { get; set; }
    public string StudentName { get; set; }
    public List<ExamMark> examResults { get; set; }
}

SqlDataReader

   SqlDataReader reader = command.ExecuteReader();
   if (reader.HasRows)
   {
      while (reader.Read())
      {

      }
   } 

参考文献

  1. LINQ: Fill objects from left join
  2. Complex GROUP BY on DataTable

【问题讨论】:

  • 最好的方法?最易读?最好的表现?
  • @Xeano。可读性最高,性能可接受
  • 实例化新对象并将值从数据库分配给属性有什么问题?
  • @Lijo 迭代返回的结果集如何增加数据库调用?
  • @Lijo 只是为了让您知道 linq 在内部使用循环。见stackoverflow.com/questions/3156059/…

标签: c# sql-server linq stored-procedures


【解决方案1】:

使用 ADO.Net 实体框架和 SPTE(Stored Proc To Enity)等方法可用于构建对象。

我有一个基于实体框架定义动态构建对象和关系的项目。这更有活力。

我可以在 code.google.com 上分享代码。

【讨论】:

    【解决方案2】:

    我认为您的问题的重点在于从 DataReader(在本例中为 SqldataReader)创建对象。

    Here 你可以找到我关于类似论点的答案之一。正如我每次所说,答案可能取决于您需要解决方案的上下文。据我所知,您不想使用 ORM,因此一个干净且基本的解决方案可能是:

    1. 使用SqlDataReaderToObject
    2. 使用Extension methods for IDbComannd and IDataReader

    其中的每一个都是基于 ADO.NET 对象的“帮助者”。正如我之前所说,使用这样的解决方案需要一些限制,例如对象字段或属性名称等。 希望这可以帮助。

    【讨论】:

      【解决方案3】:

      你真的应该考虑使用dapper。它supports stored procedures 并且正如页面所示,极其简单的 dapper 调用和过度设计的手绘解决方案之间的性能差异可以忽略不计:

      SELECT 映射超过 500 次迭代的性能 - POCO 序列化

      • 手工编码(使用 SqlDataReader)47 毫秒
      • Dapper ExecuteMapperQuery 49 毫秒

      【讨论】:

        【解决方案4】:

        这应该可以完成工作:

        using (SqlDataReader reader = command.ExecuteReader())
                {
                    var records = (from record in reader.Cast<DbDataRecord>()
                                   select new
                                   {
                                       StudentID = record.GetInt32(0),
                                       StudentName = record.GetString(1),
                                       ExamID = record.GetInt32(2),
                                       ExamName = record.GetString(3),
                                       Mark = record.GetInt32(4)
                                   })
                                   .GroupBy(r => new { StudentID = r.StudentID, StudentName = r.StudentName })
                                   .Select(
                                             r => new Student
                                             {
                                                 StudentID = r.Key.StudentID,
                                                 StudentName = r.Key.StudentName,
                                                 examResults = r.Select(e => new ExamMark
                                                 {
                                                     ExamID = e.ExamID,
                                                     ExamName = e.ExamName,
                                                     Mark = e.Mark
                                                 }).ToList()
                                             });
                }
        

        【讨论】:

          【解决方案5】:

          好吧,我不会那样做的,

          我有两个陈述

          -- Student Statement
          SELECT
                       S.StudentID,
                       S.StudentName
              FROM
                       Student S
              WHERE
                       EXISTS (
                        SELECT * FROM StudentExam SE WHERE SE.StudentID = S.Student.ID);
          
          -- Exam Statement
          SELECT
                      SE.StudentID,
                      E.ExamID,
                      E.ExamName,
                      SE.Mark 
              FROM
                      StudentExam SE
                  JOIN
                      Exam E
                          ON E.ExamID = SE.ExamID;
          

          然后,我会有一个函数来做这个,

          private IEnumerable<Tuple<int, ExamMark>> GetMarks()
          {
              ... setup the exam command here
              var reader = examCommand.ExecuteReader();
              while (reader.Read())
              {
                  yield return Tuple.Create(
                      reader.GetInt32(0),
                      new ExamMark
                          {
                              reader.GetInt32(1),
                              reader.GetString(2),
                              reader.GetInt32(3)
                          });
              }
          }
          

          那我就得调用这个函数了,

          private IEnumerable<Student> GetStudents()
          {
              var resultLookup = GetMarks().ToLookup(t => t.Item1, t => t.Item2);
          
              ... setup the student command here
              var reader = studentCommand.ExecuteReader();
              while (reader.Read())
              {
                  var studentId = reader.GetInt32(0);
                  yield return new Student
                          {
                              studentId,
                              reader.GetString(1),
                              resultLookup[studentId].ToList()
                          });
              }
          }
          

          如果您愿意,您可以在一个存储过程中完成所有操作并返回多个结果集。

          【讨论】:

          • @Lijo,你对这个答案有什么顾虑?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-06-21
          • 2016-06-12
          • 1970-01-01
          • 1970-01-01
          • 2013-02-28
          相关资源
          最近更新 更多