C# 在ASP.NET中从数据库获取一行数据的最有效方法

C# 在ASP.NET中从数据库获取一行数据的最有效方法,c#,asp.net,optimization,data-access,C#,Asp.net,Optimization,Data Access,我正在编写一个从数据库返回“资产”行的方法。它包含字符串、整数和字节数组(可以是图像/电影/文档) 现在,对于大多数行访问,我使用以下方法返回NameValueCollection,因为它是一个轻量级对象,易于使用并转换int和string public static NameValueCollection ReturnNameValueCollection(Database db, DbCommand dbCommand) { var nvc = ne

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

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

        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;
    }
创建一个DataTable然后返回它的第一个datarow看起来确实有点浪费

有没有更好的办法

我在想可能是一本对象字典,然后我手动将其每个成员都转换为对象

看看其他人是如何解决这一问题的会很有趣。我知道这属于微观优化领域,只要我不为每一行查询返回数据集(希望每次在一行代码中看到都有一英镑),就应该可以了

也就是说,这种方法很可能被用于在一个框上分配站点时分配数据访问查询

干杯


史蒂夫

你正在演示的是一种代码气味,叫做。创建自定义类型并从存储库方法返回该类型。不要试图过于笼统。。。您最终将把这种复杂性推到业务代码中,因为您将使用纯粹的过程代码与实体进行交互。更好的方法是创建业务模型对象


如果您担心太多的数据访问代码,请考虑使用ORM框架为您生成这些代码。您不应该让这种担忧影响应用程序层的糟糕设计。

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

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等来引用资产信息。

缓存数据比优化返回一行的效果要好得多。如果您是按主键选择的,则不太可能看到返回DataTable、DataRow或自定义对象之间的任何区别。我觉得这是过早的优化。我会更明确一些,但我不确定混合中的字节数组是否会改变情况。

进展如何

在数据库中没有表示行的对象容器是有原因的吗?在解决方案的其他层中,创建自定义对象更容易处理。因此,采用这种方法,有两种非常可行的解决方案

假设您有一个表示数据库中产品的自定义对象。您可以这样定义对象:

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);
    }
}
您可以像这样填写产品集合(集合):

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);
    }
}
var collection=new collection();
使用(var reader=command.ExecuteReader()){
while(reader.Read()){
var product=新产品();
int ordinal=reader.GetOrdinal(“ProductID”);
if(!reader.IsDBNull(序数){
product.ProductID=reader.GetInt32(序号);
}
序号=reader.GetOrdinal(“名称”);
if(!reader.IsDBNull(序数)){
product.Name=reader.GetString(序号);
}
ordinal=reader.GetOrdinal(“图像”);
if(!reader.IsDBNull(序数)){
var sqlBytes=reader.GetSqlBytes(序号);
product.Image=sqlBytes.Value;
}
收集。添加(产品);
}
}
请注意,我正在通过读卡器的Getx检索一个值,其中x是我要从列中检索的类型。这是Microsoft建议的每列(第二段)检索数据的方法,因为检索到的值不必装箱到System.Object中,也不必解装箱到基元类型中

由于您提到在ASP.NET应用程序中将多次调用此方法,因此您可能需要重新考虑这种通用方法。在这种情况下(以及在许多其他情况下),用于返回NameValueCollection的方法非常无效。更不用说您在不考虑当前用户的区域性的情况下将每个数据库列转换为字符串,并且区域性是ASP.NET应用程序中的一个重要考虑因素。我认为此NameValueCollection不应也用于您的其他开发工作。我可以继续讲下去,但我不会再多说了


当然,如果您要创建直接映射到表的对象,您最好查看或。您会很高兴您这样做的。

感谢所有的输入人员。我知道ORM可能是一种方式,而MVC框架是我列表中的下一个

更详细地说,我展示的代码来自我的数据访问层中的helpers部分,该部分随后将行或名称值集合传递到业务层以转换为对象

我认为mnero0429和balabaster代码就是一个例子