| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- using System.Data;
- using Npgsql;
- namespace praktika
- {
- public partial class Form1 : Form
- {
- public const string SchemaName = "dyomin_be";
- public const string AdminLogin = "admin";
- public const string AdminPassword = "admin";
- public const string ConnectionString =
- "Host=edu-pg.itiscaf.ru;Port=5432;Database=dbwork;Username=dyomin_be;Password=$vAx&y5zxQ$;Search Path=dyomin_be";
- public readonly TextBox loginTextBox = new();
- public readonly TextBox passwordTextBox = new();
- public readonly Button loginButton = new();
- public readonly Label loginStatusLabel = new();
- public readonly Panel loginPanel = new();
- public readonly Panel mainPanel = new();
- public readonly ComboBox tableComboBox = new();
- public readonly DataGridView dataGridView = new();
- public readonly Button refreshButton = new();
- public readonly Button addButton = new();
- public readonly Button saveButton = new();
- public readonly Button deleteButton = new();
- public readonly Button logoutButton = new();
- public readonly Label currentUserLabel = new();
- public readonly Dictionary<string, TableInfo> tables = new()
- {
- ["authors"] = new("id"),
- ["books"] = new("id"),
- ["tags"] = new("id"),
- ["book_tags"] = new("book_id", "tag_id") { AutoIncrementKeys = [] },
- ["comments"] = new("id"),
- ["users"] = new("id") { AdminOnly = true },
- ["v_book_catalog"] = new() { ReadOnly = true }
- };
- public DataTable? currentTable;
- public bool isAdmin;
- public string currentLogin = "";
- public Form1()
- {
- InitializeComponent();
- BuildUi();
- ShowLogin();
- }
- public void BuildUi()
- {
- Text = "PostgreSQL CRUD";
- Width = 1100;
- Height = 700;
- StartPosition = FormStartPosition.CenterScreen;
- MinimumSize = new Size(900, 550);
- loginPanel.Dock = DockStyle.Fill;
- var loginBox = new TableLayoutPanel
- {
- Width = 360,
- Height = 210,
- Anchor = AnchorStyles.None,
- ColumnCount = 1,
- RowCount = 7,
- Padding = new Padding(20)
- };
- loginBox.Location = new Point((loginPanel.Width - loginBox.Width) / 2, (loginPanel.Height - loginBox.Height) / 2);
- loginBox.RowStyles.Add(new RowStyle(SizeType.Absolute, 24));
- loginBox.RowStyles.Add(new RowStyle(SizeType.Absolute, 36));
- loginBox.RowStyles.Add(new RowStyle(SizeType.Absolute, 24));
- loginBox.RowStyles.Add(new RowStyle(SizeType.Absolute, 36));
- loginBox.RowStyles.Add(new RowStyle(SizeType.Absolute, 36));
- loginBox.RowStyles.Add(new RowStyle(SizeType.Absolute, 24));
- loginBox.RowStyles.Add(new RowStyle(SizeType.Absolute, 36));
- loginBox.Controls.Add(new Label { Text = "Логин", Dock = DockStyle.Fill }, 0, 0);
- loginTextBox.Dock = DockStyle.Fill;
- loginBox.Controls.Add(loginTextBox, 0, 1);
- loginBox.Controls.Add(new Label { Text = "Пароль", Dock = DockStyle.Fill }, 0, 2);
- passwordTextBox.Dock = DockStyle.Fill;
- passwordTextBox.UseSystemPasswordChar = true;
- loginBox.Controls.Add(passwordTextBox, 0, 3);
- loginButton.Text = "Войти";
- loginButton.Dock = DockStyle.Fill;
- loginButton.Click += LoginButton_Click;
- loginBox.Controls.Add(loginButton, 0, 4);
- loginBox.Controls.Add(new Label { Text = "Админ: admin / admin", Dock = DockStyle.Fill }, 0, 5);
- loginStatusLabel.ForeColor = Color.DarkRed;
- loginStatusLabel.Dock = DockStyle.Fill;
- loginBox.Controls.Add(loginStatusLabel, 0, 6);
- loginPanel.Controls.Add(loginBox);
- Controls.Add(loginPanel);
- mainPanel.Dock = DockStyle.Fill;
- mainPanel.Visible = false;
- var root = new TableLayoutPanel
- {
- Dock = DockStyle.Fill,
- ColumnCount = 1,
- RowCount = 2,
- Padding = new Padding(10)
- };
- root.RowStyles.Add(new RowStyle(SizeType.Absolute, 42));
- root.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
- var toolbar = new FlowLayoutPanel
- {
- Dock = DockStyle.Fill,
- FlowDirection = FlowDirection.LeftToRight,
- WrapContents = false
- };
- currentUserLabel.AutoSize = true;
- currentUserLabel.Padding = new Padding(0, 8, 16, 0);
- toolbar.Controls.Add(currentUserLabel);
- tableComboBox.Width = 180;
- tableComboBox.DropDownStyle = ComboBoxStyle.DropDownList;
- tableComboBox.SelectedIndexChanged += TableComboBox_SelectedIndexChanged;
- toolbar.Controls.Add(tableComboBox);
- refreshButton.Text = "Обновить";
- refreshButton.Click += RefreshButton_Click;
- toolbar.Controls.Add(refreshButton);
- addButton.Text = "Добавить";
- addButton.Click += AddButton_Click;
- toolbar.Controls.Add(addButton);
- saveButton.Text = "Сохранить";
- saveButton.Click += SaveButton_Click;
- toolbar.Controls.Add(saveButton);
- deleteButton.Text = "Удалить";
- deleteButton.Click += DeleteButton_Click;
- toolbar.Controls.Add(deleteButton);
- logoutButton.Text = "Выйти";
- logoutButton.Click += LogoutButton_Click;
- toolbar.Controls.Add(logoutButton);
- dataGridView.Dock = DockStyle.Fill;
- dataGridView.AllowUserToAddRows = false;
- dataGridView.AllowUserToDeleteRows = false;
- dataGridView.AutoGenerateColumns = true;
- dataGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
- dataGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
- dataGridView.MultiSelect = true;
- root.Controls.Add(toolbar, 0, 0);
- root.Controls.Add(dataGridView, 0, 1);
- mainPanel.Controls.Add(root);
- Controls.Add(mainPanel);
- AcceptButton = loginButton;
- }
- public void LoginButton_Click(object? sender, EventArgs e)
- {
- loginStatusLabel.Text = "";
- var login = loginTextBox.Text.Trim();
- var password = passwordTextBox.Text;
- if (login == AdminLogin && password == AdminPassword)
- {
- isAdmin = true;
- currentLogin = login;
- OpenMain();
- return;
- }
- try
- {
- using var connection = new NpgsqlConnection(ConnectionString);
- connection.Open();
- using var command = new NpgsqlCommand(
- $"select count(*) from {Q(SchemaName)}.{Q("users")} " +
- $"where login = @login and password_hash = crypt(@password, password_hash)",
- connection);
- command.Parameters.AddWithValue("login", login);
- command.Parameters.AddWithValue("password", password);
- var exists = Convert.ToInt32(command.ExecuteScalar()) > 0;
- if (!exists)
- {
- loginStatusLabel.Text = "Неверный логин или пароль";
- return;
- }
- isAdmin = false;
- currentLogin = login;
- OpenMain();
- }
- catch (Exception ex)
- {
- loginStatusLabel.Text = ex.Message;
- }
- }
- public void OpenMain()
- {
- loginPanel.Visible = false;
- mainPanel.Visible = true;
- currentUserLabel.Text = isAdmin ? $"Админ: {currentLogin}" : $"Пользователь: {currentLogin}";
- AcceptButton = saveButton;
- tableComboBox.Items.Clear();
- foreach (var table in tables.Where(x => isAdmin || !x.Value.AdminOnly).Select(x => x.Key))
- {
- tableComboBox.Items.Add(table);
- }
- if (tableComboBox.Items.Count > 0)
- {
- tableComboBox.SelectedIndex = 0;
- }
- }
- public void ShowLogin()
- {
- mainPanel.Visible = false;
- loginPanel.Visible = true;
- loginTextBox.Text = "";
- passwordTextBox.Text = "";
- loginStatusLabel.Text = "";
- dataGridView.DataSource = null;
- currentTable = null;
- AcceptButton = loginButton;
- loginTextBox.Focus();
- }
- public void TableComboBox_SelectedIndexChanged(object? sender, EventArgs e)
- {
- LoadCurrentTable();
- }
- public void RefreshButton_Click(object? sender, EventArgs e)
- {
- LoadCurrentTable();
- }
- public void AddButton_Click(object? sender, EventArgs e)
- {
- if (currentTable is null || SelectedTable().ReadOnly)
- {
- return;
- }
- currentTable.Rows.Add(currentTable.NewRow());
- }
- public void DeleteButton_Click(object? sender, EventArgs e)
- {
- if (currentTable is null || SelectedTable().ReadOnly)
- {
- return;
- }
- foreach (DataGridViewRow gridRow in dataGridView.SelectedRows)
- {
- if (gridRow.DataBoundItem is DataRowView rowView)
- {
- rowView.Row.Delete();
- }
- }
- }
- public void SaveButton_Click(object? sender, EventArgs e)
- {
- if (currentTable is null)
- {
- return;
- }
- dataGridView.EndEdit();
- if (BindingContext is { } context && context[currentTable] is { } manager)
- {
- manager.EndCurrentEdit();
- }
- try
- {
- var tableName = SelectedTableName();
- var table = SelectedTable();
- if (table.ReadOnly)
- {
- throw new InvalidOperationException("Эту таблицу нельзя редактировать");
- }
- using var connection = new NpgsqlConnection(ConnectionString);
- connection.Open();
- using var transaction = connection.BeginTransaction();
- foreach (DataRow row in currentTable.Rows.Cast<DataRow>().Where(x => x.RowState == DataRowState.Deleted).ToList())
- {
- DeleteRow(connection, transaction, tableName, table, row);
- }
- foreach (DataRow row in currentTable.Rows.Cast<DataRow>().Where(x => x.RowState == DataRowState.Added).ToList())
- {
- InsertRow(connection, transaction, tableName, table, row);
- }
- foreach (DataRow row in currentTable.Rows.Cast<DataRow>().Where(x => x.RowState == DataRowState.Modified).ToList())
- {
- UpdateRow(connection, transaction, tableName, table, row);
- }
- transaction.Commit();
- LoadCurrentTable();
- MessageBox.Show("Сохранено", "Готово", MessageBoxButtons.OK, MessageBoxIcon.Information);
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.Message, "Ошибка сохранения", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- }
- public void LogoutButton_Click(object? sender, EventArgs e)
- {
- isAdmin = false;
- currentLogin = "";
- tableComboBox.Items.Clear();
- ShowLogin();
- }
- public void LoadCurrentTable()
- {
- if (tableComboBox.SelectedItem is not string tableName)
- {
- return;
- }
- try
- {
- var table = tables[tableName];
- var orderBy = table.PrimaryKeys.Count > 0
- ? " order by " + string.Join(", ", table.PrimaryKeys.Select(Q))
- : "";
- using var connection = new NpgsqlConnection(ConnectionString);
- connection.Open();
- using var command = new NpgsqlCommand($"select * from {Q(SchemaName)}.{Q(tableName)}{orderBy}", connection);
- using var reader = command.ExecuteReader();
- currentTable = new DataTable(tableName);
- currentTable.Load(reader);
- PrepareTable(tableName, currentTable);
- dataGridView.DataSource = currentTable;
- dataGridView.ReadOnly = table.ReadOnly;
- addButton.Enabled = !table.ReadOnly;
- saveButton.Enabled = !table.ReadOnly;
- deleteButton.Enabled = !table.ReadOnly;
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.Message, "Ошибка загрузки", MessageBoxButtons.OK, MessageBoxIcon.Error);
- }
- }
- public void PrepareTable(string tableName, DataTable dataTable)
- {
- var table = tables[tableName];
- foreach (DataColumn column in dataTable.Columns)
- {
- column.ReadOnly = false;
- column.AllowDBNull = true;
- }
- foreach (var key in table.PrimaryKeys)
- {
- if (dataTable.Columns.Contains(key))
- {
- dataTable.Columns[key]!.ReadOnly = table.AutoIncrementKeys.Contains(key);
- }
- }
- }
- public void InsertRow(NpgsqlConnection connection, NpgsqlTransaction transaction, string tableName, TableInfo table, DataRow row)
- {
- var columns = currentTable!.Columns
- .Cast<DataColumn>()
- .Select(x => x.ColumnName)
- .Where(x => !table.AutoIncrementKeys.Contains(x) && row[x] != DBNull.Value)
- .ToList();
- if (columns.Count == 0)
- {
- throw new InvalidOperationException("Нет данных для добавления");
- }
- var columnList = string.Join(", ", columns.Select(Q));
- var valueList = string.Join(", ", columns.Select((_, index) => $"@p{index}"));
- using var command = new NpgsqlCommand(
- $"insert into {Q(SchemaName)}.{Q(tableName)} ({columnList}) values ({valueList})",
- connection,
- transaction);
- AddParameters(command, columns, row, DataRowVersion.Current, 0);
- command.ExecuteNonQuery();
- }
- public void UpdateRow(NpgsqlConnection connection, NpgsqlTransaction transaction, string tableName, TableInfo table, DataRow row)
- {
- var columns = currentTable!.Columns
- .Cast<DataColumn>()
- .Select(x => x.ColumnName)
- .Where(x => !table.PrimaryKeys.Contains(x))
- .ToList();
- if (columns.Count == 0)
- {
- return;
- }
- var setSql = string.Join(", ", columns.Select((column, index) => $"{Q(column)} = @p{index}"));
- var whereSql = BuildWhere(table, row, DataRowVersion.Original, columns.Count);
- using var command = new NpgsqlCommand(
- $"update {Q(SchemaName)}.{Q(tableName)} set {setSql} where {whereSql}",
- connection,
- transaction);
- AddParameters(command, columns, row, DataRowVersion.Current, 0);
- AddParameters(command, table.PrimaryKeys, row, DataRowVersion.Original, columns.Count);
- command.ExecuteNonQuery();
- }
- public void DeleteRow(NpgsqlConnection connection, NpgsqlTransaction transaction, string tableName, TableInfo table, DataRow row)
- {
- var whereSql = BuildWhere(table, row, DataRowVersion.Original, 0);
- using var command = new NpgsqlCommand(
- $"delete from {Q(SchemaName)}.{Q(tableName)} where {whereSql}",
- connection,
- transaction);
- AddParameters(command, table.PrimaryKeys, row, DataRowVersion.Original, 0);
- command.ExecuteNonQuery();
- }
- public static string BuildWhere(TableInfo table, DataRow row, DataRowVersion version, int offset)
- {
- if (table.PrimaryKeys.Count == 0)
- {
- throw new InvalidOperationException("Для CRUD нужен первичный ключ");
- }
- return string.Join(" and ", table.PrimaryKeys.Select((key, index) => $"{Q(key)} = @p{offset + index}"));
- }
- public static void AddParameters(NpgsqlCommand command, List<string> columns, DataRow row, DataRowVersion version, int offset)
- {
- for (var i = 0; i < columns.Count; i++)
- {
- var value = row[columns[i], version];
- command.Parameters.AddWithValue($"p{offset + i}", value is DBNull ? DBNull.Value : value);
- }
- }
- public string SelectedTableName()
- {
- return tableComboBox.SelectedItem as string
- ?? throw new InvalidOperationException("Таблица не выбрана");
- }
- public TableInfo SelectedTable()
- {
- return tables[SelectedTableName()];
- }
- public static string Q(string identifier)
- {
- return "\"" + identifier.Replace("\"", "\"\"") + "\"";
- }
- public sealed class TableInfo
- {
- public TableInfo(params string[] primaryKeys)
- {
- PrimaryKeys = primaryKeys.ToList();
- AutoIncrementKeys = primaryKeys.ToList();
- }
- public List<string> PrimaryKeys { get; init; }
- public List<string> AutoIncrementKeys { get; init; }
- public bool AdminOnly { get; init; }
- public bool ReadOnly { get; init; }
- }
- }
- }
|