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

时间:2014-07-11 02:34:33

标签: c# .net generics orm ado.net

我经常指出,在开发连接到MS SQL数据库以获取基本CRUD函数的应用程序时,我需要遍历返回的结果并填充与表设计匹配的特定数据类型的列表。 / p>

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

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

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

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

1 个答案:

答案 0 :(得分:1)

我同意微观ORM可以解决您的情况或给您一些好主意的评论。 Massive非常有趣,因为它适合一个文件。它使用框架的dynamic功能来解决您的问题。 Dapper面向映射方面。

另外,请查看Data Access Application Block。虽然现在是Open Source,但它最初是由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;
    }
}

因此,上面的代码将是一个帮助库,只写一次。那么你的应用程序中的mapper可能看起来像;

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 映射器,因为读取器上的迭代也可以被抽象化。