使用 ADO.NET、Generic 从不同的表中获取 SQL 数据

Posted

技术标签:

【中文标题】使用 ADO.NET、Generic 从不同的表中获取 SQL 数据【英文标题】:Fetching SQL data from different tables with ADO.NET, Generics 【发布时间】:2014-07-11 02:34:32 【问题描述】:

在开发连接到 MS SQL 数据库以实现基本 CRUD 功能的应用程序时,我经常遇到这样一个问题,我需要遍历返回的结果并填充与表设计匹配的特定数据类型的列表。

特定于 .NET,我只需创建一个方法(例如 GetAllObjA())并使用 ADO.NET 指定一个连接到存储过程的 SqlCommand,以从返回的 SqlDataReader 中获取数据。然后,我将遍历返回的行,创建一个新对象并将其添加到每个对象的列表中。

但是,如果我想获取不同数据类型的数据,我会为 GetAllObjB()、GetAllObjC() 等方法重写此方法,列表的数据类型不同,这感觉完全是浪费重写代码。

我意识到泛型在这里有一个目的,可以在一个方法中指定数据类型,例如 GetAll()。不幸的是,这仍然需要我定义我将从哪个表中获取数据,并且仍然需要我将列名与对象中的成员名匹配,这并不能真正解决问题,因为应用程序代码无法知道桌子是如何设计的。

这是泛型在这种情况下有用的程度吗?由于我正在构建的应用程序规模相当小,如果可以手动编写解决方案,我认为 ORM 是没有必要的。

【问题讨论】:

查看诸如 Massive、Dapper 和 PetaPoco 之类的微型 ORM 以获得实际使用或只是灵感。它们是解决此问题的轻量级解决方案。 您研究过 EntityFramework 吗?它几乎是经典 ADO 的继承者 【参考方案1】:

我同意 micro-ORM 可以解决您的场景或给您一些好主意的 cmets。 Massive 读起来很有趣,因为它可以放在一个文件中。它使用框架的dynamic 功能来解决您的问题。 Dapper 面向映射方面。

另外,看看Data Access Application Block。虽然现在是开源的,但它最初是由 Microsoft 维护的。它是一个整体的企业应用程序框架,因此您不需要几个臃肿的依赖项。但是数据访问块有一些很好的原型来满足您的要求:使用泛型将IDataReader 结果集映射到 POCO。因此,您只需编写一次映射代码,您为每个表定义的唯一内容就是实际的 reader-to-poco 属性映射。

有时表或其映射可能有一些怪癖,因此您希望手动保留映射定义。对于其他具有基本原语的简单表,使用 Rob Connery 的 Massive 演示的动态与通用行集映射器相结合可以生成一些非常易于阅读和维护的代码。

免责声明:这不是对这种方法优于 EF 的判断。它只是建议一种简单的非 EF 方法。

因此,您可能有一个定义接口的小型库:

public interface IResultSetMapper<TResult>

    Task<List<TResult>> MapSetAsync(IDataReader reader);

以及处理任何阅读器的通用 ADO.Net 辅助类:

// a method with a class that manages the Command (_Command) and Connection objects 
public async Task<List<TOut>> ExecuteReaderAsync<TOut>(IResultSetMapper<TOut> resultsMapper)

    List<TOut> results = null;
    try
    
        using(var connection = await _DbContext.CreateOpenedConnectionAsync())
        
            _Command.Connection = connection;

            // execute the reader and iterate the results; when done, the connection is closed
            using(var reader = await _Command.ExecuteReaderAsync())
            
                results = await resultsMapper.MapSetAsync(reader);
            
        
        return results;
    
    catch(Exception cmdEx)
    
        // handle or log exception...
        throw;
    

因此,上面的代码将是一个辅助库,只需编写一次。然后你的应用程序中的映射器可能看起来像;

internal class ProductReaderMap : IResultSetMapper<Product>

    public async Task<List<Product>> MapSetAsync(IDataReader reader)
    
        List<Product> results = new List<Product>();
        using(reader)
        
          results.Add(new Product
          
            ProductId = r.GetInt32(0),
            ProductName = r.GetString(1),
            SupplierId = r.GetInt32(2),
            UnitsInStock = r.GetInt16(3)
          );
        
    return results;
    

您可以进一步打破这一点,定义一个行映射器而不是一个行 set 映射器,因为对读取器的迭代也可以被抽象出来。

【讨论】:

以上是关于使用 ADO.NET、Generic 从不同的表中获取 SQL 数据的主要内容,如果未能解决你的问题,请参考以下文章

C# ado.net基础 删除一行数据 在sqlsever中的一个表中

ADO.Net 数据库查询

如何使用 ADO.NET 从具有一对多关系的 DAL 层中的多个表中返回数据

如何在 ADO.NET 中安全地创建从更改中选择的表的查询?

如何在 ADO.NET 源 SSIS 中传递参数

ADO.NET之使用DataSet类更新数据库