数据库
首页 > 数据库> > c#-使用方法为SqlDataReader中的每一行调用委托的缺点是什么?

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

作者:互联网

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

这就是为什么有一个新想法并准备在一个新的大型项目中使用它的原因,我需要您对此有意见,尤其是负面的看法.

很长时间以来,我无聊地反复键入或在必须直接访问数据库的项目中复制粘贴以下块:

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

第二种方法是:

>写得更短,
>更易于阅读(至少对我来说;有些人可能会说,实际上,它的可读性要差得多),
>更难出错(例如,在第一种情况下,我经常忘记使用连接之前打开连接,或者忘记阻塞时等等),
>在Intellisense的帮助下更快
>更加简洁,特别是对于简单的请求.

例:

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

在小型项目中使用简单的ExecuteNonQuery,ExecuteScalar和ReadManyRows以及通用的DataAccess< ReadManyRows来实现这种事情之后,我很高兴看到代码更短并且更易于维护. 我发现只有两个缺点:
>对要求的某些修改将需要对代码进行大量更改.例如,如果需要添加事务,则使用普通的SqlCommand方法将非常容易.如果改用我的方法,则将需要重写整个项目以使用SqlCommands和事务.
>在命令级别上进行少量修改将需要从我的方法过渡到标准SqlCommands.例如,仅查询一行时,要么必须扩展DataAccess类以包括这种情况,要么代码必须直接将SqlCommand与ExecuteReader(CommandBehavior.SingleRow)一起使用.
>可能会损失少量性能(我还没有精确的指标).

这种方法的其他弱点是什么,尤其是对于DataAccess< ReadManyRows&gt ;?

解决方法:

您想要实现的效果很好,我实际上很喜欢这种语法,并且我认为它非常灵活.但是,我相信您需要更好地设计API.

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

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

使用匿名类型而不是字典

编写一个将匿名类型转换为字典的助手很简单,但我认为它可以大大改善表示法,并且您无需编写新的Dictionary< string,object>.

使用Tuple.Create

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

在DataReader周围创建一个强类型的包装器

这将消除所有地方的丑陋转换,实际上,您是否真的需要访问该lambda中的DataReader?

我将通过代码为您的示例进行说明.
赞誉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调用中实例化,并为选择器lambda提供方便(且受限制的)对基础DataReader的访问.

标签:sqlconnection,maintainability,generics,readability,c
来源: https://codeday.me/bug/20191023/1913717.html