Когда я нахожу новую идею, я всегда придерживаюсь ее и не вижу никаких слабых сторон. Плохие вещи случаются, когда я начинаю использовать новую идею в большом проекте и позже обнаруживаю некоторых мотылек, что идея была очень плохая, и я не должен использовать ее в каком-либо проекте.Каковы недостатки использования метода, который вызывает делегат для каждой строки в 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"]);
}));
Второй подход:
Короче писать,
Легче читать (по крайней мере для меня; некоторые люди могут сказать, что на самом деле это гораздо менее читаемо),
Сложнее делать ошибки (например, в первом случае я часто забываю открыть соединение перед его использованием, или я забыл
while
блок и т. д.),быстрее с помощью 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<T>.ReadManyRows
, я был рад видеть, что код намного короче и проще поддерживать.
я нашел только два недостатка:
Некоторые изменения в требованиях потребует тяжелых изменений кода. Например, если есть необходимость добавлять транзакции, это будет очень легко сделать с обычным подходом
SqlCommand
. Если вместо этого используется мой подход, потребуется переписать весь проект для использованияSqlCommand
s и транзакций.Незначительные изменения на уровне команд потребуют перехода от моего подхода к стандарту
SqlCommand
. Например, при запросе только для одной строки необходимо либо расширить классDataAccess
, либо включить кодSqlCommand
вместоExecuteReader(CommandBehavior.SingleRow)
.Может быть небольшая потеря производительности (пока у меня нет точных показателей).
Каковы другие слабые стороны этого подхода, особенно для DataAccess<T>.ReadManyRows
?
Для меня сложнее узнать, что происходит во втором блоке кода. – CRice
@CRice - Это можно решить, переработав API. Одной из возможностей было бы использование свободного интерфейса для добавления некоторого синтаксического сахара в код клиента: 'new DataAccess (« выберите отдельное ProductName из Shop.Product »). Где ({" @shopId ", this.Shop.Id}). execute (dataReader => {return (string) dataReader ["ProductName"];}) '. Это не правильно C#, и это смешивает два примера, но я надеюсь, что это демонстрирует тактику. –
@ Давид Харкнесс: спасибо за идею. На самом деле, я нахожу его более читаемым, чем то, что я сделал. –