C# 使用 SqlDataReader 从 SQL Server 检索数据到主详细信息列表

Posted

技术标签:

【中文标题】C# 使用 SqlDataReader 从 SQL Server 检索数据到主详细信息列表【英文标题】:C# retrieve data from SQL Server using SqlDataReader to Master Detail List 【发布时间】:2021-12-14 05:03:42 【问题描述】:

我有 MasterDetail 课程:

class Master

    public int ID  get; set; 
    public string Name  get; set; 
    public List<Detail> Details  get; set; 


class Detail

    public Description  get; set; 
    public Amount  get; set; 

我现在使用下面的方法并且工作正常。

List<Master> result = new List<Master>();

// SQL Connection 

string sqlCommand = "SELECT * FROM Master LEFT JOIN Detail on Master.ID = Detail.ID";

using (System.Data.SqlClient.SqlDataReader dr = db.DbDataReader as System.Data.SqlClient.SqlDataReader)

    if (dr.HasRows)
    
        Master LastMaster = null;

        while (dr.Read())
        
            if (LastMaster == null || Convert.ToInt(dr["ID"]) != LastMaster.ID)
            
                Master h = new Master();
                h.ID = Convert.ToInt(dr["ID"]);
                h.Name = Convert.ToString(dr["Name"]);
                result.Add(h);
                
                LastMaster = h;
                       
            
            if (dr["Description"] == DBNull.Value)
                continue;
                
            if (h.Detail == null)
                h.Detail = new List<Detail>();
            
            Detail d = new Detail();
            d.Description = dr["Description"] as string;
            d.Amount = Convert.ToDouble(dr["Amount"]);
            
            LastMaster.Detail.Add(d);
            ......

        
           
    .....

有没有更好的方法来填充 C# 中的列表对象列表?我很感激任何建议。谢谢。

【问题讨论】:

可以使用对象关系映射器,如Entity Framework或Dapper 【参考方案1】:

您可以在场景中使用 Dapper(一种微型 ORM)。下面是示例代码

const string createSql = @"
create table #Users (Id int, Name varchar(20))
create table #Posts (Id int, OwnerId int, Content varchar(20))

insert #Users values(99, 'Sam')
insert #Users values(2, 'I am')

insert #Posts values(1, 99, 'Sams Post1')
insert #Posts values(2, 99, 'Sams Post2')
insert #Posts values(3, null, 'no ones post')";

using(var connection = new SqlConnection("database connection string"))

    
    connection.Execute(createSql);
    try
    
        const string sql =@"select * from #Posts p 
                                left join #Users u on u.Id = p.OwnerId 
                            Order by p.Id";

        var data = connection.Query<Post, User, Post>(sql, (post, user) =>  post.Owner = user; return post; ).ToList();
    
    catch(Exception ex)

【讨论】:

【参考方案2】:

Ibram 评论了 EF 和 Dapper,Abu 为 Dapper 提供了一个示例(但我不确定它是否演示了生成一个具有单个主控和每个主控多个详细信息的图形,就像您所做的那样 - 如果您愿意,dapper 可以这样做探索它)

在 EF 中我们可以这样做:

安装 EF 核心电动工具 - 因为您已经有一个 db,我们将使用它来生成类。这个操作可以通过命令行来完成,但是 EFCPT 让很多操作变得更容易 右键单击您的项目,选择 EF Core Power Tools .. 逆向工程师 填写新的连接字符串详细信息 选择您希望转换为类的数据库对象 根据需要设置其他选项(稍后您可以找到更多关于它们的信息,如果您的 db 表类似于 Orders、Customers、Company 并且您希望您的类称为 Order/Customer/Company(类不应该有复数名称)。现在勾选“将连接字符串放入代码” - 您可以稍后将其删除到配置文件中 完成。最终,您将获得一些类和一个上下文,其中包含 OnModelCreating 中的大量代码,这些代码对表、列、键、关系中的所有内容进行了描述。..

现在您可以运行一些查询,例如:

var c = new YourContext();
var ms = c.Masters.Include(m => m.Details).ToList();

这基本上与您发布的内容相同

您可以通过塑造更复杂的 linq 查询来获得更多技巧:

var q = c.Masters.Include(m => m.Details)
  .Where(m => m.Name.StartsWith("Smith"))
  .OrderBy(m => m.Name);

var ms = q.ToList();

它会被翻译成类似的东西

SELECT * FROM master join detail on ...
WHERE name LIKE 'Smith%'
ORDER BY m.name

如果您检查qDebugView 属性,您可以看到生成的查询

您可以进行更改:

ms[0].Details.Clear(); //causes delete of all details for this master
ms[1].Details.Add(new Detail  someprop = some value); //causes insert of new details for this master
ms[2].Name = "Hello"; //causes update of this master name
c.SaveChanges(); //carries out the above, in sql, to affect the db

当您操作返回的对象并保存时,EF 将酌情删除/插入/更新,以将数据库与对象发生的情况同步。重要的是您要了解 EF 会跟踪它创建的所有对象发生的情况,以便它可以执行此操作


什么时候使用 EF,什么时候使用 Dapper?好吧,它不一定是相互排斥的。您可以在同一个项目中使用它们。一般来说,我会说使用 EF(或其他类似的 ORM - nHibernate 是另一种流行的,适用于将 linq 表达式转换为 sql 并将数据跟踪回对象的类似概念)用于 sql 如此简单的东西不必编写、跟踪它并将更改写回,这是一种生产力提升。它不打算用于形成无法很好地映射到客户端对象的 hoc 查询。为此,您可以使用 Dapper,或者您可以形成客户端对象并将它们添加到 EF 的模型中,然后运行填充它们的原始 sql。 Dapper 速度很快,因为它不会跟踪更改、映射或连接复杂的对象图;你手动完成所有这些。 Dapper 对原始 sql 进行了方便的抽象并创建了类,但 EF 走得更远;这是有代价的 - EF 非常方便,但重量要重得多。

【讨论】:

以上是关于C# 使用 SqlDataReader 从 SQL Server 检索数据到主详细信息列表的主要内容,如果未能解决你的问题,请参考以下文章

SqlDataReader 和 T-SQL:在使用来自 C# 的“异步”调用时使用“try...catch”和“throw”时不会传播异常

C#:使用 SqlDataReader 在图表上显示 SQL 查询

C#利用SqlDataReader读取SQL Server数据表

c# - 从 SqlDataReader 填充通用列表

在 C# 中将 SQL Datareader 转换为数据集

c#你如何从sqldatareader返回数据集?