Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
0 / 0 / 0
Регистрация: 03.10.2024
Сообщений: 6

Преобразование переменной типа object, разработка универсального метода для работы с бд

03.10.2024, 19:32. Показов 728. Ответов 3

Студворк — интернет-сервис помощи студентам
Все доброго времени суток!
Пишу программу на C# (windows Form). Программа должна работать с БД. Пытаюсь сделать универсальные методы,
принимающий в качестве параметров объекты типа object. Пользователь может из вне передать как string так и int.
Подскажите, нужна ли здесь преобразование (явное) типов для "Par_1" и "Par_2" ? И как это лучше реализовать? параметр "st" не в счет.
Внизу один из методов, которые планирую использовать для работы с БД
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 public List<string[]> Zapr_Select(string ComandSelect, object Par_1, object Par_2, object st)
 {
     SqlDataReader reader;
     List<string[]> array = new List<string[]>();
     //string[,] array=new string[str,st];
     string Conectingstring = Properties.Settings.Default.Par_ConnectingString;
     SqlConnection connection = new SqlConnection(Conectingstring);
     SqlCommand command = new SqlCommand(ComandSelect, connection);
     command.Parameters.AddWithValue("@Par_1", Par_1);
     command.Parameters.AddWithValue("@Par_2", Par_2);
     connection.Open();
     reader = command.ExecuteReader();
     if (reader.HasRows)
     {
 
         while (reader.Read())
         {
             string[] str_tab = new string[(int)st];
             for (int i = 0; i < (int)st; i++)
             {
                 str_tab[i] = reader.GetValue(i).ToString();
             }
             array.Add(str_tab);
         }
         reader.Close();
     }
     connection.Close();
 
 
     return array;
 }
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
03.10.2024, 19:32
Ответы с готовыми решениями:

Приведение переменной типа object к переменной производного типа в приложенном коде
Добрый день! Столкнулся с проблемой. Есть обработчик события. Обработчик события общий для двух издателей, но издатели разного типа(Пусть...

Разработка программы для работы с файлом без типа
1)Создать файл, компоненты (количество &lt;=10) которого являются слова. После создания файла значения его компонент с четными номерами...

Разработка программы для работы с файлом без типа
Построить файл, компоненты (&lt;=7) которого являются натуральными числами, после чего заменить значение последней компоненты файла на число...

3
Нарушитель
110 / 86 / 32
Регистрация: 10.05.2023
Сообщений: 323
03.10.2024, 21:32
Лучший ответ Сообщение было отмечено Mihail_Smmirnov как решение

Решение

В вашем методе Zapr_Select явное преобразование типов для параметров Par_1 и Par_2 не требуется, так как метод AddWithValue класса SqlParameter автоматически определяет тип данных переданного объекта. Однако, если вы хотите более строго контролировать типы данных, можно использовать явное преобразование.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public List<string[]> Zapr_Select(string ComandSelect, object Par_1, object Par_2, int st)
{
    SqlDataReader reader;
    List<string[]> array = new List<string[]>();
    string Conectingstring = Properties.Settings.Default.Par_ConnectingString;
    SqlConnection connection = new SqlConnection(Conectingstring);
    SqlCommand command = new SqlCommand(ComandSelect, connection);
 
    // Явное преобразование типов
    if (Par_1 is int)
    {
        command.Parameters.Add("@Par_1", SqlDbType.Int).Value = (int)Par_1;
    }
    else if (Par_1 is string)
    {
        command.Parameters.Add("@Par_1", SqlDbType.NVarChar).Value = (string)Par_1;
    }
 
    if (Par_2 is int)
    {
        command.Parameters.Add("@Par_2", SqlDbType.Int).Value = (int)Par_2;
    }
    else if (Par_2 is string)
    {
        command.Parameters.Add("@Par_2", SqlDbType.NVarChar).Value = (string)Par_2;
    }
 
    connection.Open();
    reader = command.ExecuteReader();
    if (reader.HasRows)
    {
        while (reader.Read())
        {
            string[] str_tab = new string[st];
            for (int i = 0; i < st; i++)
            {
                str_tab[i] = reader.GetValue(i).ToString();
            }
            array.Add(str_tab);
        }
        reader.Close();
    }
    connection.Close();
 
    return array;
}
1
4 / 3 / 1
Регистрация: 26.06.2022
Сообщений: 18
04.10.2024, 00:02
Тут просто криком кричит разделение метода на несколько и использование полиморфизма. Нарушается принцип единой ответственности.
Я бы делал так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
internal static class TypeExtension
{
    private static readonly Dictionary<Type, SqlDbType> _types = new()
    {
        { typeof(int), SqlDbType.Int },
        { typeof(string), SqlDbType.NVarChar },
    };
 
    public static SqlDbType ToSqlDbType(this Type type)
    {
        if (!_types.TryGetValue(type, out var sqlType))
            throw new ArgumentException($"Unsupported type {type}");
            
        return sqlType;
    }
}
 
public class SqlTest : IDisposable
{
    
 
    private readonly SqlConnection _sqlConnection;
    public SqlTest()
    {
        var connectionString = Properties.Settings.Default.Par_ConnectingString;
        _sqlConnection = new SqlConnection(connectionString);
        _sqlConnection.Open();
    }
 
    public List<string[]> Zapr_Select<T1, T2>(string ComandSelect, T1 Par_1, T2 Par_2, int st)
        where T1 : notnull
        where T2 : notnull
    {
        var command = new SqlCommand(ComandSelect, _sqlConnection);
 
        command.Parameters.Add("@Par_1", Par_1.GetType().ToSqlDbType()).Value = Par_1;
        command.Parameters.Add("@Par_2", Par_2.GetType().ToSqlDbType()).Value = Par_1;
 
        var result = GetCommandResult(command, st);
        return result;
    }
 
    private List<string[]> GetCommandResult(SqlCommand command, int st)
    {
        using var reader = command.ExecuteReader(); //Если поддерживается IDisposable
        var result = new List<string[]>();
 
        if (!reader.HasRows()) return result;
 
        while (reader.Read())
        {
            string[] str_tab = new string[st];
            for (int i = 0; i < st; i++)
            {
                str_tab[i] = reader.GetValue(i).ToString();
            }
            result.Add(str_tab);
        }
 
        return result;
    }
 
    #region Dispose
 
    private bool _disposed;
 
    ~SqlTest()
    {
        Dispose(false);
    }
 
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
 
    protected virtual void Dispose(bool disposing)
    {
        if (_disposed) return;
        if (disposing)
        {
            //dispose managed state (managed objects)
            _sqlConnection.Close();
        }
        //free unmanaged resources (unmanaged objects) and override finalizer
        //set large fields to null
 
        _disposed = true;
    }
 
    #endregion
}
1
Эксперт .NET
 Аватар для Wolfdp
3790 / 1767 / 371
Регистрация: 15.06.2012
Сообщений: 6,543
Записей в блоге: 3
04.10.2024, 14:30
Ну, самое простое решение (если не считать всякие ORM), это явно указать методы которые строго будут принимать явно ожидаемый тип данных, а внутри уже использовать обобщенный универсальный метод. Делается это для следующего:
- "внешний" код явно будет знать какие данные нужно передать. Это очень облегчает и чтение, и использование, и сопровождение кода.
- "внешний" код не сможет передать не валидные данные, т.к. метод подразумевает что нужно передать число/дату/строку, и запихнуть иное не получится чисто технически.
- вопрос парсинга того что вводит юзер -- это исключительно UI забота. Уровень БД не должен ничего знать о том, в каком виде вводятся данные.

Обобщеные методы выглядят так

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
abstract class BaseRepository
{
    //TODO: connection string need read from config
    protected virtual string ConnectionString { get; } = "Data Source=.;Initial Catalog=NyaDataBase;User id=sa;Password=123;";
 
    protected int Execute(string sql, params (string, object)[] parameters)
    {
        using var command = Init(ConnectionString, sql);
        SetParameters(command, parameters);
        return command.ExecuteNonQuery();
    }
    protected IEnumerable<T> Execute<T>(string sql, Func<SqlDataReader, T> parser, params (string, object)[] parameters)
    {
        using var command = Init(ConnectionString, sql);
        SetParameters(command, parameters);
        var reader = command.ExecuteReader();
 
        while (reader.Read())
            yield return parser(reader);
    }
 
    private static SqlCommand Init(string connectionString, string sql)
    {
        var connection = new SqlConnection(connectionString);
        try
        {
            connection.Open();
            var command = new SqlCommand(sql, connection);
            command.Disposed += (_, __) => connection.Dispose();
            return command;
        }
        catch 
        {
            connection.Dispose();
            throw;
        }
    }
    private static void SetParameters(SqlCommand command, params (string, object)[] parameters)
    {
        if (parameters.Any() != true)
            return;
 
        foreach ((var name, var value) in parameters)
            command.Parameters.AddWithValue(name, value);
    }
}
Execute -- просто выполнение без ожидания возращаемых данных (такие как inser, update, delete)
Execute<T> -- выполнение с вычиткой через SqlDataReader. Ожидает делегат, который будет каждую строку парсить в ожидаемый тип T

Оба метода ожидают набор ИмяПараметра+Значение в виде массива через params. В методе SetParameters просто проходим массив (если он есть) и записываем все значения в command.Parameters

SqlCommand Init(string connectionString, string sql) -- тупо для избегания дублирования кода и одного места инициализации подключения (если вдруг нужно будет вносить правки, то только в один метод, а не несколько).

Теперь как всё это добро использовать дальше. Допустим у нас есть таблица пользователей Users, в которой храниться логин, пароль, роль пользователя и скажем временная метка последнего изменения записи. Добавим класс для отображения данных.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
enum Roles
{
    Admin,
    Moderator,
    User
}
 
class UserDto
{
    public required string Login { get; set; }
    public required string Password { get; set; }
    public required Roles Role { get; set; }
    public required DateTime TimeStamp { get; set; }
}
И также для удобства добавим статический класс, который будет знать про имя таблицы в БД и имена колонок
C#
1
2
3
4
5
6
7
8
9
static class UsersTbl
{
    public const string Name = "Users";
 
    public const string LoginCln = "Login";
    public const string PasswordCln = "Password";
    public const string RoleCln = "Role";
    public const string TimeStampCln = "TimeStamp";
}
И теперь допустим нам нужно два метода для работы с таблицей:
- найти пользователя по логину и паролю
- добавить нового пользователя

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
class UserRepository : BaseRepository
{
    public UserDto? FindByLoginAndPassword(string login, string password)
    {
        const string loginPr = "login";
        const string passwordPr = "password";
 
        var sql = $"""
            SELECT
            [{UsersTbl.LoginCln}],
            [{UsersTbl.PasswordCln}],
            [{UsersTbl.RoleCln}],
            [{UsersTbl.TimeStampCln}]
            FROM [{UsersTbl.Name}]
            WHERE
            [{UsersTbl.LoginCln}] = @{loginPr}
            AND [{UsersTbl.PasswordCln}] = @{passwordPr}
            """;
 
        static UserDto Map(SqlDataReader reader)
        {
            var i = 0;
            return new UserDto
            {
                Login = reader.GetString(i++),
                Password = reader.GetString(i++),
                Role = (Roles)reader.GetInt32(i++),
                TimeStamp = reader.GetDateTime(i++)
            };
        }
 
        var user = Execute(sql, Map,
                (loginPr, login),
                (passwordPr, password)
            ).FirstOrDefault();
 
        return user;
    }
 
    public void Add(string login, string password, Roles role)
    {
        const string loginPr = "login";
        const string passwordPr = "password";
        const string rolePr = "role";
        const string timeStampPr = "timeStamp";
 
        var sql = $"""
            INSERT INTO [{UsersTbl.Name}]
            (
                [{UsersTbl.LoginCln}],
                [{UsersTbl.PasswordCln}],
                [{UsersTbl.RoleCln}],
                [{UsersTbl.TimeStampCln}]
            )
            VALUES
            (
                @{loginPr},
                @{passwordPr},
                @{rolePr},
                @{timeStampPr}
            )
            """;
 
        _ = Execute(sql,
            (loginPr, login),
            (passwordPr, password),
            (rolePr, (int)role),
            (timeStampPr, DateTime.UtcNow));
    }
}
var sql = $""" -- как можно заметить, тут я напихал констатнт. Делается это для того чтобы нельзя было допустить опечатку в имени колонки. Также облегчает нахождение где она используется (в сопровождении кода это ппц как помогает, особенно когда нужно переименовать колонку в БД).

Map -- локальный статический метод, который передается в качестве делегата для парсинга при вычитке. Т.к. он внутри другого метода, это "изолирует" его. В теории можно вынести в просто статический метод, если повторяется.

Так как тот же метод Add явно ожидает конкретных параметров конкретного типа, дальше не будет проблемы с угадыванием "шо за чем передавать".

В целом код "облегчается" в использовании до такой схемы:
- добавили нужный метод
- объявили переменные
- написали sql
- пнули нужный Execute
- ....
- PROFIT!

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
P.S. можно конечно написать "проще", вплоть до впихивание всего в условный ClickHandler. Правда с таким подходом чем больше пишешь, тем больнее становится. Данный пример -- минимальная попытка избежания боли в дальнейшем.

P.P.S. В идеале стоит разобраться в какой-нибудь ORM и работать через неё.
2
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
04.10.2024, 14:30
Помогаю со студенческими работами здесь

Разработка программы для работы с файлом без типа
Создать файл, компоненты (количество &lt;=9) которого являются вещественными числами. После создания файла значение минимальной компоненты...

Разработка программы для работы с файлом без типа
СРОЧНО!!! Помогите пожалуйста.... оч прошу... Создать файл, компоненты (количество &lt;=10) которого являются вещественными числами....

Преобразование object -> byte[] -> object для передачи по сети
Дело такое: пишется прога для обращения к базе по сетке, ну а там нужна криптография!!!:confused: Т.е. надо вызвать метод удалённого...

Вывод переменной типа object
Есть переменная alc типа object, в ней объявлена переменная типа y:double ее необходимо вывести на label Такой вывод не срабатывает ...

Преобразование типа функции (метода)
Задача заставить код работать (: #include &lt;iostream&gt; #include &lt;iterator&gt; #include &lt;algorithm&gt; #include &lt;functional&gt; ...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
4
Ответ Создать тему
Новые блоги и статьи
SDL3 для Desktop (MinGW): Рисуем цветные прямоугольники с помощью рисовальщика SDL3 на Си и C++
8Observer8 17.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-rectangles-sdl3-c. zip finish-rectangles-sdl3-cpp. zip
Символические и жёсткие ссылки в Linux.
algri14 15.03.2026
Существует два типа ссылок — символические и жёсткие. Ссылка в Linux — это запись в каталоге, которая может указывать либо на inode «файла-ИСТОЧНИКА», тогда это будет «жёсткая ссылка» (hard link),. . .
[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ФедосеевПавел 14.03.2026
Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора ВВЕДЕНИЕ Выполняя задание на управление насосной группой заполнения резервуара,. . .
делаю науч статью по влиянию грибов на сукцессию
anaschu 13.03.2026
прикрепляю статью
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru