Это займет некоторое повторение работы, но вот несколько идей о том, как справиться с этим.Это не приведёт код в идеальное состояние, но может сделать его немного более управляемым.Одна из проблем заключается в том, что у каждого метода есть части в двух местах - в методе и в другой, где команда хранится в словаре.
- Никогда не добавляйте больше SQL в этот класс.Начните определять и использовать новые репозитории, которые вы хотите.
- Легко и издеваться.Вы можете использовать рефакторинг extract interface , чтобы создать интерфейс, позволяющий имитировать этот класс, даже в его текущей форме.Это будет большой, уродливый интерфейс, но, по крайней мере, вы можете имитировать методы, если вам это нужно.
Это простая часть.Как можно реорганизовать весь класс, не нарушая ни одной его части?Эти шаги являются лишь некоторыми идеями:
Первый шаг - просто ввести строку подключения, необходимую классу:
public class YourDataAccessClass
{
private readonly string _connectionString;
public YourDataAccessClass(string connectionString)
{
_connectionString = connectionString;
}
}
Вы будете использовать ее по одному методу за раз.Изначально вы можете оставить большую часть класса, включая словарь, как есть.Таким образом, методы, которые вы не изменили, продолжат работать.
Затем вы можете открыть класс в двух отдельных окнах, чтобы вы могли видеть функцию словаря, содержащую SQL, и функции, которые его используют.бок о бок.Это будет намного сложнее, если вам придется прокручивать вверх и вниз.
Скорее всего, вы захотите переместить SQL для каждой функции в эту функцию.Вы могли бы сделать это, когда вы рефакторинг каждой функции, но это может быть менее болезненным, чтобы сделать все сразу, чтобы вы получили эффективность от повторения.
Вы можете определить новую переменную в каждой функции, скопировать и вставить:
var sql = "SELECT * FROM User WHERE User.UserID = @id;";
(Опять же, не так, как я обычно это пишу.)
Теперьу вас есть функция или 100 функций, которые выглядят следующим образом:
public void deleteUser(int userId)
{
var sql = "DELETE User WHERE User.UserID = @id;";
if (_cmds.TryGetValue("deleteUser", out SqlCommand cmd))
{
lock(cmd)
{
cmd.Parameters[0].Value = userId;
cmd.ExecuteNonQuery();
}
}
}
Для команд, не связанных с запросом, вы можете написать такую функцию в своем классе, которая исключит повторяющийся код для открытия соединения,создайте команду и т.д:
private void ExecuteNonQuery(string sql, Action<SqlCommand> addParameters = null)
{
using (var connection = new SqlConnection(_connectionString))
using (var command = new SqlCommand(sql))
{
addParameters?.Invoke(command);
connection.Open();
command.ExecuteNonQuery();
}
}
Сохраните следующий фрагмент кода.Вы могли бы даже быть в состоянии держать это в буфере обмена большую часть времени.Вставьте его в каждый из ваших методов, не связанных с запросом, прямо под SQL:
ExecuteNonQuery(sql, command =>
{
});
После того, как вы вставите его, переместите строку или строки, которые добавляют параметры, в тело аргумента cmd
(которыйс именем cmd
, чтобы вы могли перемещать строки без изменения имени переменной), а затем удалить существующий код, который выполнял запрос ранее.
ExecuteNonQuery(sql, cmd =>
{
cmd.Parameters[0].Value = userId;
});
Теперь ваша функция выглядит следующим образом:
public void deleteUser(int userId)
{
var sql = "DELETE User WHERE User.UserID = @id;";
ExecuteNonQuery(sql, cmd =>
{
cmd.Parameters[0].Value = userId;
});
}
Я не говорю, что это забавно, но это сделает процесс редактирования этих функций более эффективным, поскольку вы печатаете меньше и просто перемещаете вещи одним и тем же способом снова и снова.
Те, которые на самом деле возвращают данные, менее забавны, но все же управляемы.
Во-первых, возьмите в значительной степени тот же самый шаблонный код.Вероятно, это можно улучшить, потому что он все еще немного повторяется, но, по крайней мере, он более автономен:
using (var connection = new SqlConnection(_connectionString))
using (var cmd = new SqlCommand(sql)) // again, named "cmd" on purpose
{
connection.Open();
}
Начиная с этого:
public int isConnected(int userId, out int name)
{
var sql = "SELECT * FROM User WHERE User.UserID = @id;";'
bool result = false;
amount = 0;
if (_cmds.TryGetValue("userInfo", out SqlCommand cmd))
{
lock (cmd)
{
cmd.Parameters[0].Value = userId;
using (SqlDataReader reader = new cmd.ExecuteReader())
{
if (reader.HasRows)
while (reader.Read())
{
amount = (int)Math.Round(reader.GetDecimal(0));
result = reader.GetInt32(1);
}
}
}
}
}
Вставьте шаблон в метод:
public int isConnected(int userId, out int name)
{
var sql = "SELECT * FROM User WHERE User.UserID = @id;";'
bool result = false;
amount = 0;
using (var connection = new SqlConnection(_connectionString))
using (var cmd = new SqlCommand(sql)) // again, named "cmd" on purpose
{
connection.Open();
}
if (_cmds.TryGetValue("userInfo", out SqlCommand cmd))
{
lock (cmd)
{
cmd.Parameters[0].Value = userId;
using (SqlDataReader reader = new cmd.ExecuteReader())
{
if (reader.HasRows)
while (reader.Read())
{
amount = (int)Math.Round(reader.GetDecimal(0));
result = reader.GetInt32(1);
// was this a typo? The code in the question doesn't
// return anything or set the "out" variable. But
// if that's in the method then that will be part of
// what gets copied.
}
}
}
}
}
Затем, как и прежде, переместите деталь, в которой вы добавляете свои параметры выше connection.Open();
, и переместите деталь, где вы используете команду, прямо под connection.Open();
и удалите то, что осталось.Результат таков:
public int isConnected(int userId, out int name)
{
var sql = "SELECT * FROM User WHERE User.UserID = @id;";'
bool result = false;
amount = 0;
using (var connection = new SqlConnection(_connectionString))
using (var cmd = new SqlCommand(sql)) // again, named "cmd" on purpose
{
cmd.Parameters[0].Value = userId;
connection.Open();
using (SqlDataReader reader = new cmd.ExecuteReader())
{
if (reader.HasRows)
while (reader.Read())
{
amount = (int)Math.Round(reader.GetDecimal(0));
result = reader.GetInt32(1);
}
}
}
}
Вы, вероятно, можете попасть в паз и сделать это через минуту или две, что означает, что это займет всего несколько часов.
После того, как всеиз этого вы можете удалить свою огромную функцию словаря.Теперь класс зависит от введенной строки соединения и обычно открывает и закрывает соединения, а не сохраняет соединение и использует его снова и снова.
Вы также можете разбить его.Один из способов - переместить строку соединения и вспомогательную функцию в базовый класс (или просто дублировать вспомогательную функцию - она действительно мала), и вы можете переместить любую функцию запроса в меньший класс, поскольку каждая функция является автономной.