我正在编写一种从数据库中返回“资产”行的方法。它包含字符串,整数和字节数组(可以是图像/电影/文档)。
现在对于大多数行访问,我使用以下方法返回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;
}
现在我对这种数据访问的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;
}
创建DataTable然后返回其第一个数据行似乎有点浪费。
有更好的方法吗?
我想的可能是一个对象词典,然后我手动转换每个成员。
看看别人如何解决这个问题会很有趣。我知道这种情况属于微优化领域,只要我没有为每一行查询返回DataSet(希望每次我在一行代码中看到它都有一个磅)它应该没问题。
据说这个方法很可能被调用在一个盒子上的所有站点上进行数据访问查询。
干杯
史蒂夫
答案 0 :(得分: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 SQL或ADO.NET Entity Framework。你会很开心的。
答案 1 :(得分:3)
就代码效率而言,你可能在最少的键击中完成它,虽然看起来很浪费,但可能是最简单的维护。但是,如果你只关注只做必要的效率,你可以创建一个轻量级的struct / class来填充数据并使用类似的东西:
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等...
答案 2 :(得分:2)
您所展示的是一种名为Primitive Obsession的代码气味。创建自定义类型并从存储库方法返回该类型。不要试图过于通用......您最终会将这种复杂性推到业务代码中,因为您将使用纯粹的过程代码与您的实体进行交互。最好创建为您的业务建模的对象。
如果您担心过多的数据访问代码,请查看使用ORM框架来为您生成此代码。你不应该让这个问题在你的应用层中规定糟糕的设计。
答案 3 :(得分:0)
通过缓存数据,您将获得比尝试优化返回单行更多的好处。如果您是通过主键选择,那么您不太可能看到返回DataTable或DataRow或自定义对象之间的任何区别。这让我觉得过早优化。我会更明确,但我不确定混合中是否有一个字节数组会改变。
答案 4 :(得分:0)
感谢所有输入人员。我知道ORM可能是要走的路,而MVC框架就在我的列表中。
为了提供更多细节,我展示的代码来自我的数据访问层中的帮助器部分,然后将行或名称值集合传递给业务层以转换为对象。
我认为mnero0429和balabaster代码示例给了我正确的方向。使用datareader并手动获取数据,而不会弄乱中间对象。感谢详细的MS链接mnero0429。对于主要的痴迷是公平的 - 所以我确实在商业层中从中做出了适当的资产类别;)
我也将研究ADO实体框架。
再一次,感谢您的建议 - 我知道即使我使用DataSet.Tables [0] .Rows [0] [“bob”]或者其他一些这样的世界会继续转动但是当你得到那个痒 - 什么是最好的做法就是把它弄得很好!