5

我正在编写一种从数据库返回“资产”行的方法。它包含字符串、整数和字节数组(这可能是图像/电影/文档)。

现在对于大多数行访问,我使用以下方法,它返回一个 NameValueCollection,因为它是一个轻量级对象,易于使用并转换为 int 和字符串。

        public static NameValueCollection ReturnNameValueCollection(Database db, DbCommand dbCommand)
    {

        var nvc = new NameValueCollection();

        using (IDataReader dr = db.ExecuteReader(dbCommand))
        {
            if (dr != null)
            {
                 while (dr.Read())
                 {
                     for (int count = 0; count < dr.FieldCount; count++)
                     {
                         nvc[dr.GetName(count)] = dr.GetValue(count).ToString();
                     }
                 }
            }
        }

        dbCommand.Dispose();
        return nvc.Count != 0 ? nvc : null;
    }

现在我对这种数据访问的方法通常是获取返回数据行的方法。

       public static DataRow ReturnDataRow(Database db, DbCommand dbCommand)
    {
        var dt = new DataTable();

        using (IDataReader dr = db.ExecuteReader(dbCommand))
            if (dr != null) dt.Load(dr);

        dbCommand.Dispose();
        return dt.Rows.Count != 0 ? dt.Rows[0] : null;
    }

创建一个 DataTable 然后返回它的第一个数据行似乎有点浪费。

有没有更好的方法来做到这一点?

我在想可能是一个对象字典,然后我手动转换每个成员。

看看其他人如何解决这个问题会很有趣。我知道这有点属于微优化领域,只要我不为每个行查询返回数据集(希望我每次在一行代码中看到它都有一磅)它应该没问题。

也就是说,可能会调用此方法来分配一个盒子上的站点上的数据访问查询。

干杯

史蒂夫

4

5 回答 5

7

怎么样了?

您没有代表数据库中一行的对象容器是否有原因?在解决方案的其他层中创建自定义对象要容易得多。因此,采用这种方法,您的问题有两种非常可行的解决方案。

假设您有一个代表数据库中产品的自定义对象。您将像这样定义对象:

public class Product {
    public int ProductID { get; set; }
    public string Name { get; set; }
    public byte[] Image { get; set; }
}

你会像这样填写一组产品(集合):

var collection = new Collection<Product>();

using (var reader = command.ExecuteReader()) {
    while (reader.Read()) {
        var product = new Product();

        int ordinal = reader.GetOrdinal("ProductID");
        if (!reader.IsDBNull(ordinal) {
            product.ProductID = reader.GetInt32(ordinal);
        }

        ordinal = reader.GetOrdinal("Name");
        if (!reader.IsDBNull(ordinal)) {
            product.Name = reader.GetString(ordinal);
        }

        ordinal = reader.GetOrdinal("Image");
        if (!reader.IsDBNull(ordinal)) {
            var sqlBytes = reader.GetSqlBytes(ordinal);
            product.Image = sqlBytes.Value;
        }

        collection.Add(product);
    }
}

请注意,我正在通过读者的 Get x检索一个值,其中x是我要从列中检索的类型。这是 Microsoft 推荐的按http://msdn.microsoft.com/en-us/library/haa3afyz.aspx(第二段)检索列数据的方法,因为检索到的值不必装箱到 System.Object并拆箱成原始类型。

由于您提到此方法将在 ASP.NET 应用程序中被调用很多次,您可能需要重新考虑这样的通用方法。您用来返回NameValueCollection的方法在这种情况下(并且可以说在许多其他情况下)表现不佳。更不用说在不考虑当前用户的文化的情况下将每个数据库列转换为字符串,而文化是 ASP.NET 应用程序中的一个重要考虑因素。我认为这个NameValueCollection也不应该用于您的其他开发工作。我可以继续说下去,但我会为你省去我的咆哮。

当然,如果您要创建直接映射到表的对象,您不妨研究一下LINQ to SQLADO.NET Entity Framework。你会很高兴你做到了。

于 2009-04-16T18:16:58.490 回答
3

就代码效率而言,您可能用最少的按键完成了它,虽然这看起来很浪费,但可能是最容易维护的。但是,如果您只关心只做严格必要的事情的效率,您可以创建一个轻量级的结构/类来填充数据并使用类似于:

public class MyAsset
{
    public int ID;
    public string Name;
    public string Description;
}

public MyAsset GetAsset(IDBConnection con, Int AssetId)
{
    using (var cmd = con.CreateCommand("sp_GetAsset"))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add(cmd.CreateParameter("AssetID"));
        using(IDataReader dr = cmd.ExecuteReader())
        {
            if (!dr.Read()) return null;

            return new MyAsset() { 
                ID = dr.GetInt32(0), 
                Name = dr.GetString(1), 
                Description = dr.GetString(2)
            };
        }
    }
}

同样,您可以以类似的方式将数据转储到您的 KVP 集合中......

它不像您的原始代码那样干净,但它不会创建整个表只是为了获得单行......

正如在另一篇关于代码气味的帖子中提到的那样,我可能不会将命令作为参数传递,我认为我更有可能将命令封装在此方法中,仅传递数据库连接和 id我想要的资产——假设我当然没有使用缓存,并传回 MyAsset 实例。这使得该方法足够通用,可以在任何数据库类型上使用 - 当然假设存储过程存在。这样,我的其余代码就不需要知道数据库的任何类型,除了它是什么类型的数据库......在我的应用程序的其余部分,我可以使用 MyAssetInstance.ID、MyAssetInstance.Name、 MyAssetInstance.Description 等...

于 2009-04-16T17:56:58.850 回答
2

您要演示的是一种称为Primitive Obsession的代码气味。创建一个自定义类型并从您的存储库方法中返回它。不要试图过于通用......您最终只会将这种复杂性推入您的业务代码,因为您将使用纯程序代码与您的实体进行交互。更好地创建对您的业务建模的对象。

如果您担心数据访问代码过多,请考虑使用 ORM 框架来为您生成此代码。您不应该让这种担忧决定应用程序层中的不良设计。

于 2009-04-16T17:55:57.730 回答
0

感谢所有输入的家伙。我知道 ORM 可能是要走的路,而 MVC 框架在我的列表中是下一个。

更详细地说,我展示的代码来自数据访问层中的帮助程序部分,然后将行或名称值集合传递给业务层以转换为对象。

我认为 mnero0429 和 balabaster 代码示例给了我正确的方向。使用数据读取器并像这样手动获取数据,而不会弄乱中间对象。感谢您提供详细的 MS 链接 mnero0429。对原始的痴迷很公平-尽管我确实在业务层中从中做出了适当的资产类别;)

我也将研究 ADO 实体框架。

再次感谢您的建议-我知道即使我使用 DataSet.Tables[0].Rows[0]["bob"] 或类似的东西,世界也会继续转动,但是当您感到痒时-最好的方法是什么做吧,它很高兴有它的划痕!

于 2009-04-16T23:00:00.403 回答
0

与尝试优化返回单行相比,您将从缓存数据中获得更多好处。如果您通过主键进行选择,那么您不太可能会看到返回 DataTable 或 DataRow 或自定义对象之间的任何区别。这让我觉得过早的优化。我会更确定,但我不确定混合中的字节数组是否会改变事情。

于 2009-04-16T18:01:36.627 回答