3

我有一个非常简单的映射函数,称为“BuildEntity”,它执行通常无聊的“左/右”编码,将我的阅读器数据转储到我的域对象中。(如下所示)我的问题是 - 如果我不按原样恢复此映射中的每一列,我会得到“System.IndexOutOfRangeException”异常,并想知道 ado.net 是否有任何纠正此问题的方法,所以我没有t 需要在每次调用 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
4

8 回答 8

6

另请查看我编写的用于数据命令的扩展方法:

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());

其中“products”是您要填充的 IList<Product>,“GetProduct”包含从数据读取器创建 Product 实例的逻辑。它不会帮助解决没有所有字段都存在的特定问题,但是如果您正在做很多像这样的老式 ADO.NET,它会非常方便。

于 2008-08-22T12:26:37.643 回答
1

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

于 2008-08-22T12:28:59.757 回答
1

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

于 2008-08-22T12:30:26.603 回答
1

尽管 connection.GetSchema("Tables") 确实返回有关数据库中表的元数据,但如果您定义任何自定义列,它不会返回存储过程中的所有内容。

例如,如果您输入一些随机的临时列,例如 *SELECT ProductName,'Testing' As ProductTestName FROM dbo.Products",您将不会看到 'ProductTestName' 作为列,因为它不在 Products 表的架构中。为了解决这个问题,并询问返回数据中可用的每一列,利用 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

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

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

于 2008-08-25T14:25:40.497 回答
0

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

于 2008-08-22T12:18:46.867 回答
0

在开始 while 循环之前,我会调用reader.GetOrdinal每个字段名称。不幸的是,如果该字段不存在,则GetOrdinal抛出一个IndexOutOfRangeException,因此它的性能不会很高。

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

于 2008-08-22T12:21:32.147 回答
0

如果您不想使用 ORM,您也可以对此类事情使用反射(尽管在这种情况下,因为 ProductID 在双方的名称不同,您不能以此处演示的简单方式进行操作) : C# 中的提供程序

于 2008-08-22T12:59:40.947 回答
0

I ended up writing my own, but this mapper is pretty good (and simple): https://code.google.com/p/dapper-dot-net/

于 2013-12-09T16:56:58.620 回答