代码之家  ›  专栏  ›  技术社区  ›  CountZero

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

  •  5
  • CountZero  · 技术社区  · 16 年前

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

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

            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;
        }
    

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

           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;
        }
    

    创建数据表并返回其第一个数据行似乎有点浪费。

    有更好的方法吗?

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

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

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

    干杯

    史蒂夫

    5 回复  |  直到 14 年前
        1
  •  7
  •   Michael Nero    14 年前

    怎么样?

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

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

    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应用程序中将多次调用此方法,因此您可能需要重新考虑这种通用方法。用于返回 名称值集合 在这种情况下(在许多其他情况下也可以这样说),性能非常差。更不用说,在不考虑当前用户的区域性的情况下,将每个数据库列转换为字符串,而区域性是ASP.NET应用程序中的一个重要考虑因素。我认为这个 名称值集合 也不应该在其他开发工作中使用。我可以继续说下去,但我会帮你保留我的胡言乱语。

    当然,如果您要创建直接映射到表的对象,您也可以查看 LINQ to SQL ADO.NET Entity Framework . 你会很高兴的。

        2
  •  3
  •   BenAlabaster    16 年前

    在代码效率方面,您可能只需最少的击键就可以完成这项工作,虽然这看起来很浪费,但维护起来可能最简单。但是,如果您只关心执行严格必要的操作的效率,那么您可以创建一个轻量级结构/类来填充数据,并使用类似于以下内容的内容:

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

    同样,您可以以类似的方式将数据直接转储到您的kvps集合中…

    它看起来不像原始代码那么干净,但它不会创建整个表来获取单行…

    正如在另一篇关于代码气味的文章中提到的,我可能不会将命令作为参数传入,我认为我更可能将命令封装在这个方法中,只传递数据库连接和我想要的资产的ID——当然,假设我没有使用缓存,并将myasse传回。T实例。这使得该方法足够通用,可以用于任何数据库类型——当然,假设存储过程存在。这样,我的其余代码就不需要知道关于数据库的任何信息,而只需要知道它是什么类型的数据库…在应用程序的其余部分,我可以使用myassetinstance.id、myassetinstance.name、myassetinstance.description等引用资产信息…

        3
  •  2
  •   Michael Meadows    16 年前

    你在妖魔化的是一种叫做 Primitive Obsession . 创建自定义类型并从存储库方法返回。不要过于笼统…您将最终把这种复杂性推到业务代码中,因为您将使用纯过程代码与实体进行交互。最好创建业务模型的对象。

    如果您担心过多的数据访问代码,请使用ORM框架来为您生成这些代码。您不应该让这一问题在您的应用程序层中影响糟糕的设计。

        4
  •  0
  •   Jamie Ide    16 年前

    缓存数据会比优化返回单个行带来更多好处。如果您选择的是主键,那么返回数据表、数据行或自定义对象之间不太可能有任何区别。这让我觉得是过早的优化。我会更加明确,但我不确定混合中的字节数组是否会改变事情。

        5
  •  0
  •   CountZero    16 年前

    谢谢大家的意见。我知道ORM可能是一个不错的选择,MVC框架是我的下一个选择。

    为了提供更详细的信息,我要显示的代码来自我的数据访问层中的helpers部分,然后将行或名称值集合传递给业务层以转换为对象。

    我认为mnero0429和balabaster代码示例给了我正确的方向。使用一个数据阅读器,像这样手动获取数据,而不必乱弄智能媒体对象。感谢您提供详细的MS Link mnero0429。公平对待最初的困扰-我确实在业务层从IT中创造了一个合适的资产类别;)

    我也将研究ADO实体框架。

    再次感谢您的建议-我知道即使我使用了dataset.tables[0].rows[0][“bob”]或其他类似的方法,世界也会不断地变化,但是当您感到痒时-什么是最好的办法,最好的办法就是刮一下!