gpt4 book ai didi

c# - 使用为 SqlDataReader 中的每一行调用委托(delegate)的方法有什么缺点?

转载 作者:行者123 更新时间:2023-11-30 15:09:02 25 4
gpt4 key购买 nike

当我发现一个新想法时,我总是坚持下去,看不到它的任何弱点。当我开始在一个大项目中使用这个新想法时,坏事就发生了,后来发现一些飞蛾说这个想法非常糟糕,我不应该在任何项目中使用它。

这就是为什么有了新想法并准备将其用于新的大型项目时,我需要您对此发表意见,尤其是负面意见


很长一段时间,我厌倦了在必须直接访问数据库的项目中一遍又一遍地键入或复制粘贴以下 block :

string connectionString = Settings.RetrieveConnectionString(Database.MainSqlDatabase);
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();

using (SqlCommand getProductQuantities = new SqlCommand("select ProductId, AvailableQuantity from Shop.Product where ShopId = @shopId", sqlConnection))
{
getProductQuantities.Parameters.AddWithValue("@shopId", this.Shop.Id);
using (SqlDataReader dataReader = getProductQuantities.ExecuteReader())
{
while (dataReader.Read())
{
yield return new Tuple<int, int>((int)dataReader["ProductId"], Convert.ToInt32(dataReader["AvailableQuantity"]));
}
}
}
}

所以我做了一个小类,允许编写类似的东西来做与上面相同的事情:

IEnumerable<Tuple<int, int>> quantities = DataAccess<Tuple<int, int>>.ReadManyRows(
"select ProductId, AvailableQuantity from Shop.Product where ShopId = @shopId",
new Dictionary<string, object> { { "@shopId", this.Shop.Id } },
new DataAccess<string>.Yield(
dataReader =>
{
return new Tuple<int, int>(
(int)dataReader["ProductId"],
Convert.ToInt32(dataReader["AvailableQuantity"]);
}));

第二种方法是:

  • 写起来更短,

  • 更易于阅读(至少对我而言;有些人可能会说实际上,它的可读性要差得多),

  • 更难出错(例如第一种情况,我经常在使用前忘记打开连接,或者忘记while block 等),

  • 在 Intellisense 的帮助下速度更快,

  • 更加简洁,特别是对于简单的请求。

例子:

IEnumerable<string> productNames = DataAccess<string>.ReadManyRows(
"select distinct ProductName from Shop.Product",
new DataAccess<string>.Yield(dataReader => { return (string)dataReader["ProductName"]; }));

用简单的 ExecuteNonQuery 实现这样的事情之后, ExecuteScalarReadManyRows和一个通用的 DataAccess<T>.ReadManyRows在一个小项目中,我很高兴看到代码更短且更易于维护。

我发现只有两个缺点:

  • 需求中的一些修改将需要大量代码更改。例如,如果需要添加交易,用普通的SqlCommand就很容易了。方法。如果改用我的方法,则需要重写整个项目才能使用 SqlCommand s 和交易。

  • 命令级别的轻微修改将需要从我的方法转移到标准 SqlCommand秒。例如,当只查询一行时,DataAccess必须扩展类以包含这种情况,否则代码必须直接使用 SqlCommandExecuteReader(CommandBehavior.SingleRow)相反。

  • 可能会有小的性能损失(我还没有精确的指标)。

这种方法的其他弱点是什么,特别是对于 DataAccess<T>.ReadManyRows

最佳答案

您要完成的事情很好,我实际上喜欢这种语法,而且我认为它非常灵活。但是我相信您需要更好地设计 API。

该代码可读性强,几乎很漂亮,但很难理解,主要是因为大量泛型没有多大意义,除非您确切知道每种类型的含义。我会尽可能使用泛型类型推断来消除其中的一些。为此,请考虑使用泛型方法而不是泛型类型。

一些语法建议(我现在没有编译器,所以它们基本上是想法):

使用匿名类型而不是字典

编写一个将匿名类型转换为字典的帮助器是微不足道的,但我认为它大大改进了表示法,您不需要编写 new Dictionary<string, object> .

使用 Tuple.Create

创建此静态方法是为了避免显式指定类型。

围绕 DataReader 创建一个强类型包装器

这将消除所有地方的那些丑陋的转换——实际上,您真的需要访问 DataReader 吗?在那个 lambda 中?

我将通过代码为您的示例说明这一点。
感谢David Harkness用于链接想法。

var tuples = new DataAccess ("select ProductId, AvailableQuantity from Shop.Product where ShopId = @shopId")
.With (new { shopId = this.Shop.Id }) // map parameters to values
.ReadMany (row =>
Tuple.Create (row.Value<int> ("ProductId"), row.Value<int> ("AvailableQuantity")));

var strings = new DataAccess ("select distinct ProductName from Shop.Product")
.ReadMany (row => row.Value<string> ("ProductName"));

我还可以看到它正在扩展以处理单行选择:

var productName = new DataAccess ("select ProductName from Shop.Product where ProductId = @productId")
.With (new { productId = this.SelectedProductId }) // whatever
.ReadOne (row => row.Value<string> ("ProductName"));

这是 Row 的草稿类:

class Row {
DataReader reader;

public Row (DataReader reader)
{
this.reader = reader;
}

public T Value<T> (string column)
{
return (T) Convert.ChangeType (reader [column], typeof (T));
}
}

要在ReadOne里面实例化和 ReadMany调用并提供对底层 DataReader 的方便(和有限)访问用于选择器 lambda。

关于c# - 使用为 SqlDataReader 中的每一行调用委托(delegate)的方法有什么缺点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4910333/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com