ADO.NET从SQLDataReader映射到域对象?

时间:2008-08-22 12:14:38

标签: ado.net

我有一个非常简单的映射函数,名为“BuildEntity”,它执行将我的阅读器数据转储到我的域对象所需的通常无聊的“左/右”编码。 (如下所示)我的问题是这个 - 如果我没有按原样带回这个映射中的每一列,我得到“System.IndexOutOfRangeException”异常,并想知道ado.net是否有任何要纠正的内容,所以我不知道每次调用时都需要将每列调回SQL ...

我真正想要的是像“IsValidColumn”这样的东西,所以我可以在我的DataAccess类中保留这个1映射函数,并定义所有左/右映射 - 并且即使sproc不返回每个映射函数也能工作栏目列出......

Using reader As SqlDataReader = cmd.ExecuteReader()
  Dim product As Product
  While reader.Read()
    product = New Product()
    product.ID = Convert.ToInt32(reader("ProductID"))
    product.SupplierID = Convert.ToInt32(reader("SupplierID"))
    product.CategoryID = Convert.ToInt32(reader("CategoryID"))
    product.ProductName = Convert.ToString(reader("ProductName"))
    product.QuantityPerUnit = Convert.ToString(reader("QuantityPerUnit"))
    product.UnitPrice = Convert.ToDouble(reader("UnitPrice"))
    product.UnitsInStock = Convert.ToInt32(reader("UnitsInStock"))
    product.UnitsOnOrder = Convert.ToInt32(reader("UnitsOnOrder"))
    product.ReorderLevel = Convert.ToInt32(reader("ReorderLevel"))
    productList.Add(product)
  End While

8 个答案:

答案 0 :(得分:6)

另请查看此extension method I wrote以获取数据命令:

public static void Fill<T>(this IDbCommand cmd,
    IList<T> list, Func<IDataReader, T> rowConverter)
{
    using (var rdr = cmd.ExecuteReader())
    {
        while (rdr.Read())
        {
            list.Add(rowConverter(rdr));
        }
    }
}

你可以像这样使用它:

cmd.Fill(products, r => r.GetProduct());

其中“产品”是IList&lt;产品&gt;您想要填充,“GetProduct”包含从数据读取器创建Product实例的逻辑。对于没有所有字段存在的这个特定问题,它无济于事,但如果你正在做很多像这样的老式ADO.NET,它就会非常方便。

答案 1 :(得分:1)

使用GetSchemaTable()方法检索DataReader的元数据。返回的DataTable可用于检查特定列是否存在。

答案 2 :(得分:1)

为什么不让每个sproc返回完整的列设置,使用null,-1或者没有数据的可接受值。避免必须捕获IndexOutOfRangeException或重写LinqToSql中的所有内容。

答案 3 :(得分:1)

虽然connection.GetSchema(“Tables”)确实返回有关数据库中表的元数据,但如果您定义任何自定义列,它将不会返回您的sproc中的所有内容。

例如,如果你输入一些随机ad-hoc列,如* SELECT ProductName,'Testing'作为ProductTestName FROM dbo.Products“,你将不会看到'ProductTestName'作为列,因为它不在Schema的产品表。要解决这个问题,并要求返回数据中的每个列,请利用SqlDataReader对象上的方法“GetSchemaTable()”

如果我将此添加到原始问题中列出的现有代码示例中,您会注意到在声明读者之后我添加了一个数据表以从读取器本身捕获元数据。接下来,我遍历这个元数据并将每个列添加到我在左右代码中使用的另一个表中,以检查每个列是否存在。

更新源代码

Using reader As SqlDataReader = cmd.ExecuteReader() 
Dim table As DataTable = reader.GetSchemaTable()
Dim colNames As New DataTable()
For Each row As DataRow In table.Rows
 colNames.Columns.Add(row.ItemArray(0))
Next
Dim product As Product  While reader.Read()    
product = New Product()  
If Not colNames.Columns("ProductID") Is Nothing Then
  product.ID = Convert.ToInt32(reader("ProductID"))
End If    
product.SupplierID = Convert.ToInt32(reader("SupplierID"))    
product.CategoryID = Convert.ToInt32(reader("CategoryID"))    
product.ProductName = Convert.ToString(reader("ProductName"))    
product.QuantityPerUnit = Convert.ToString(reader("QuantityPerUnit"))    
product.UnitPrice = Convert.ToDouble(reader("UnitPrice"))    
product.UnitsInStock = Convert.ToInt32(reader("UnitsInStock"))    
product.UnitsOnOrder = Convert.ToInt32(reader("UnitsOnOrder"))    
product.ReorderLevel = Convert.ToInt32(reader("ReorderLevel"))    
productList.Add(product)  
End While

老实说这是一个黑客,因为你应该返回每一列以正确地保湿你的对象。但是我想要包含这个阅读器方法,因为它实际上会抓取所有列,即使它们没有在表模式中定义。

这种将关系数据映射到域模型的方法可能会在您进入延迟加载方案时导致一些问题。

答案 4 :(得分:0)

为什么不使用LinqToSql - 您需要的一切都是自动完成的。为了通用,您可以使用任何其他ORM tool for .NET

答案 5 :(得分:0)

在开始while循环之前,我会为每个字段名称调用reader.GetOrdinal。不幸的是,如果字段不存在,GetOrdinal会抛出IndexOutOfRangeException,因此效果不会很好。

您可以将结果存储在Dictionary<string, int>中,并使用其ContainsKey方法确定该字段是否已提供。

答案 6 :(得分:0)

如果你不想使用ORM,你也可以使用反射来做这样的事情(虽然在这种情况下,因为ProductID在两边都没有相同的名称,你不能以这里演示的简单方式做到这一点): List Provider in C#

答案 7 :(得分:0)

我最终写了自己的,但这个mapper非常好(而且很简单):https://code.google.com/p/dapper-dot-net/