MySQL veritabanlarını yönetmek için profesyonel bir çözüm mü arıyorsunuz? C# ile geliştirdiğimiz bu MySQL Yönetim Aracı, veritabanı işlemlerinizi kolaylaştıran kapsamlı özellikler sunuyor. Windows Forms tabanlı bu uygulama, MySQL sunucularına bağlanma, veritabanı ve tablo yönetimi, SQL sorguları çalıştırma gibi temel işlemlerin yanı sıra gelişmiş fonksiyonlar da içeriyor.
Neden Bu MySQL Yönetim Aracını Kullanmalısınız?
Bu özel araç, MySQL veritabanlarıyla çalışan geliştiriciler ve veritabanı yöneticileri için birçok avantaj sunar:
- Kullanıcı Dostu Arayüz: Temiz ve sezgisel tasarımı sayesinde kullanımı kolaydır
- Güvenli Bağlantı: Şifreli bağlantı desteğiyle veri güvenliğini ön planda tutar
- Veri Düzenleme: Doğrudan tablo verilerini düzenleme ve silme imkanı sağlar
- Performans Optimizasyonu: Büyük veri setleriyle çalışırken optimize edilmiş performans sunar
Temel Özellikler ve Fonksiyonlar
Kolay Bağlantı Yönetimi
Araç, MySQL sunucularına hızlı bağlanma ve bağlantı bilgilerini kaydetme özelliği sunar. Son kullanılan bağlantı bilgilerini hatırlayarak zamandan tasarruf sağlar.
Veritabanı ve Tablo İşlemleri
- Veritabanı listeleme ve seçme
- Tablo görüntüleme ve yönetme
- Tablo silme ve yapısını inceleme
- Sağ tık menüleriyle hızlı işlem yapma
SQL Sorgulama Yetenekleri
- SQL komutlarını çalıştırma (F5 kısayolu desteği)
- Sorgu sonuçlarını tablo halinde görüntüleme
- SELECT, INSERT, UPDATE, DELETE gibi temel komutları destekleme
Veri Düzenleme Özellikleri
- Tablo verilerini doğrudan düzenleme
- Çoklu satır seçimi ve silme
- Birincil anahtar takibiyle güvenli veri değişikliği
Teknik Detaylar ve Kod Mimarisi
Araç, C# programlama dili ve Windows Forms teknolojisi kullanılarak geliştirilmiştir. MySQL bağlantıları için MySql.Data kütüphanesi kullanılmaktadır. Asenkron programlama teknikleri sayesinde uzun süren işlemlerde kullanıcı arayüzü donmaz.
Kod yapısında dikkat çeken bazı önemli noktalar:
- Katmanlı Mimari: Kod okunabilirliği ve bakım kolaylığı için işlevlere göre ayrılmıştır
- Hata Yönetimi: Tüm olası hatalara karşı kullanıcıyı bilgilendiren mesajlar içerir
- Performans Optimizasyonu: Büyük veri setlerinde performansı artırmak için çeşitli optimizasyonlar uygulanmıştır
Kullanım Senaryoları
Bu MySQL yönetim aracı çeşitli senaryolarda kullanılabilir:
- Web Uygulama Geliştiricileri: MySQL kullanan web projelerinde veritabanı yönetimi için
- Veri Analistleri: Veri inceleme ve düzenleme işlemleri için
- Eğitim Amaçlı: Veritabanı yönetimi öğrenenler için pratik bir araç olarak
- Küçük ve Orta Ölçekli İşletmeler: Basit veritabanı yönetim ihtiyaçları için
Sonuç
Bu MySQL Yönetim Aracı, C# ve MySQL teknolojilerini kullanarak geliştirilmiş güçlü bir veritabanı yönetim çözümüdür. Açık kaynak kodlu yapısı sayesinde geliştiriciler kendi ihtiyaçlarına göre özelleştirebilirler. Kullanıcı dostu arayüzü ve güçlü fonksiyonlarıyla MySQL veritabanı yönetimini kolaylaştıran bu araç, hem yeni başlayanlar hem de profesyoneller için ideal bir seçenektir.
using System.Data;
using System.Diagnostics;
using System.Text.Json;
using MySql.Data.MySqlClient;
namespace MySQL_Workbanch
{
public partial class Form1 : Form
{
private TextBox txtServer;
private TextBox txtUserId;
private TextBox txtPassword;
private Button btnConnect;
private Button btnDisconnect;
private ComboBox cmbDatabases;
private ComboBox cmbTables;
private TextBox txtSqlEditor;
private Button btnExecuteSql;
private Label lblStatus;
private Panel topPanel;
private Panel middlePanel;
private Panel editorPanel;
private TabControl tabResults; // Yeni TabControl
private TabPage pageQueryResults;
private TabPage pageTableStructure;
private DataGridView dgvResults; // Sonuçlar sekmesine taşınacak
private DataGridView dgvStructure; // Yeni, yapı sekmesi için
private Button btnDeleteSelectedRows; // Yeni Sil butonu
private ContextMenuStrip contextMenuDbList; // Veritabanı sağ tık menüsü
private ContextMenuStrip contextMenuTableList; // Tablo sağ tık menüsü
// --- Diğer Alanlar ---
private MySqlConnection connection;
private string currentConnectionString = ""; // Aktif bağlantı dizesi
private List<string> primaryKeyColumns = new List<string>(); // Düzenleme/Silme için PK sütunları
private string currentlyDisplayedTable = ""; // Hangi tablonun verisinin gösterildiği
private const string SettingsFileName = "mysql_admin_settings.json"; // Ayar dosyası adı
public Form1()
{
InitializeComponentManual();
this.Text = "Gelişmiş MySQL Yönetim Aracı - by Osman Bayrak (Dev)";
this.Size = new Size(950, 700); // Boyutları biraz daha artıralım
this.MinimumSize = new Size(800, 600);
this.BackColor = Color.LightSteelBlue;
this.StartPosition = FormStartPosition.CenterScreen;
this.FormClosing += Form1_FormClosing;
this.Load += Form1_Load; // Form yüklenirken ayarları oku
}
private void InitializeComponentManual()
{
// Alt Durum Çubuğu
lblStatus = new Label { Dock = DockStyle.Bottom, Text = "Bağlı Değil", Padding = new Padding(5), BackColor = Color.SlateGray, ForeColor = Color.White, Height = 25, Font = new Font(this.Font.FontFamily, 9f) };
this.Controls.Add(lblStatus);
// Üst Panel (FlowLayout ile)
#region Top Panel Setup
topPanel = new Panel { Dock = DockStyle.Top, AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink, Padding = new Padding(5), BackColor = Color.AliceBlue };
this.Controls.Add(topPanel);
FlowLayoutPanel flowTopRow = new FlowLayoutPanel { Dock = DockStyle.Top, FlowDirection = FlowDirection.LeftToRight, WrapContents = true, AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink, Padding = new Padding(0, 0, 0, 5) };
topPanel.Controls.Add(flowTopRow);
FlowLayoutPanel flowBottomRow = new FlowLayoutPanel { Dock = DockStyle.Top, FlowDirection = FlowDirection.LeftToRight, WrapContents = true, AutoSize = true, AutoSizeMode = AutoSizeMode.GrowAndShrink };
topPanel.Controls.Add(flowBottomRow);
int controlHeight = 25;
int textBoxWidth = 130;
int buttonWidth = 100;
int comboWidth = 200;
Padding controlMargin = new Padding(3, 3, 10, 3);
// Üst Satır Kontrolleri
Label lblServer = new Label { Text = "Server:", AutoSize = true, Margin = controlMargin, Anchor = AnchorStyles.Left, Height = controlHeight, TextAlign = ContentAlignment.MiddleLeft };
txtServer = new TextBox { Size = new Size(textBoxWidth, controlHeight), Margin = controlMargin }; // Default değer Load'da gelecek
Label lblUserId = new Label { Text = "User ID:", AutoSize = true, Margin = controlMargin, Anchor = AnchorStyles.Left, Height = controlHeight, TextAlign = ContentAlignment.MiddleLeft };
txtUserId = new TextBox { Size = new Size(textBoxWidth, controlHeight), Margin = controlMargin }; // Default değer Load'da gelecek
Label lblPassword = new Label { Text = "Password:", AutoSize = true, Margin = controlMargin, Anchor = AnchorStyles.Left, Height = controlHeight, TextAlign = ContentAlignment.MiddleLeft };
txtPassword = new TextBox { Size = new Size(textBoxWidth, controlHeight), Margin = controlMargin, PasswordChar = '*' };
btnConnect = new Button { Text = "Bağlan", Size = new Size(buttonWidth, controlHeight + 5), Margin = controlMargin, BackColor = Color.LightGreen, Font = new Font(this.Font, FontStyle.Bold) };
btnDisconnect = new Button { Text = "Bağlantıyı Kes", Size = new Size(buttonWidth + 25, controlHeight + 5), Margin = controlMargin, BackColor = Color.LightCoral, Enabled = false };
flowTopRow.Controls.AddRange(new Control[] { lblServer, txtServer, lblUserId, txtUserId, lblPassword, txtPassword, btnConnect, btnDisconnect });
// Alt Satır Kontrolleri
Label lblDb = new Label { Text = "Database:", AutoSize = true, Margin = controlMargin, Anchor = AnchorStyles.Left, Height = controlHeight, TextAlign = ContentAlignment.MiddleLeft };
cmbDatabases = new ComboBox { Size = new Size(comboWidth, controlHeight), Margin = controlMargin, DropDownStyle = ComboBoxStyle.DropDownList, Enabled = false };
Label lblTable = new Label { Text = "Tablo:", AutoSize = true, Margin = controlMargin, Anchor = AnchorStyles.Left, Height = controlHeight, TextAlign = ContentAlignment.MiddleLeft };
cmbTables = new ComboBox { Size = new Size(comboWidth, controlHeight), Margin = controlMargin, DropDownStyle = ComboBoxStyle.DropDownList, Enabled = false };
flowBottomRow.Controls.AddRange(new Control[] { lblDb, cmbDatabases, lblTable, cmbTables });
// Sağ Tık Menüleri
contextMenuDbList = new ContextMenuStrip();
contextMenuDbList.Items.Add("Sil", null, ContextMenuDbDelete_Click);
cmbDatabases.ContextMenuStrip = contextMenuDbList;
contextMenuTableList = new ContextMenuStrip();
contextMenuTableList.Items.Add("Sil", null, ContextMenuTableDelete_Click);
contextMenuTableList.Items.Add(new ToolStripSeparator());
contextMenuTableList.Items.Add("Veriyi Göster", null, ContextMenuTableShowData_Click);
contextMenuTableList.Items.Add("Yapıyı Göster", null, ContextMenuTableShowStructure_Click);
cmbTables.ContextMenuStrip = contextMenuTableList;
#endregion
// Orta Panel (Editör ve TabControl)
middlePanel = new Panel { Dock = DockStyle.Fill, Padding = new Padding(5, 0, 5, 5) };
this.Controls.Add(middlePanel);
// Editör Paneli (Orta panelin üstünde)
#region Editor Panel Setup
editorPanel = new Panel { Dock = DockStyle.Top, Height = 180, Padding = new Padding(10), BackColor = Color.WhiteSmoke, BorderStyle = BorderStyle.FixedSingle };
middlePanel.Controls.Add(editorPanel);
Label lblSqlEditor = new Label { Text = "SQL Komutu:", Dock = DockStyle.Top, AutoSize = true, Padding = new Padding(0, 0, 0, 5) };
editorPanel.Controls.Add(lblSqlEditor);
btnExecuteSql = new Button { Text = "Çalıştır (F5)", Dock = DockStyle.Bottom, Height = 35, BackColor = Color.SkyBlue, Font = new Font(this.Font, FontStyle.Bold), Enabled = false };
editorPanel.Controls.Add(btnExecuteSql);
txtSqlEditor = new TextBox { Dock = DockStyle.Fill, Multiline = true, ScrollBars = ScrollBars.Vertical, AcceptsReturn = true, AcceptsTab = true, Font = new Font("Consolas", 10F), Enabled = false, Margin = new Padding(0, 0, 0, 5) };
editorPanel.Controls.Add(txtSqlEditor);
txtSqlEditor.BringToFront();
txtSqlEditor.KeyDown += TxtSqlEditor_KeyDown; // F5 ile çalıştırma için
#endregion
// TabControl (Orta panelde editörün altında, kalan alanı doldurur)
#region TabControl Setup
tabResults = new TabControl { Dock = DockStyle.Fill, Margin = new Padding(0, 5, 0, 0) };
middlePanel.Controls.Add(tabResults);
tabResults.BringToFront();
// Sekme 1: Sonuçlar
pageQueryResults = new TabPage("Sonuçlar");
tabResults.TabPages.Add(pageQueryResults);
dgvResults = new DataGridView
{
Dock = DockStyle.Fill,
AllowUserToAddRows = false,
AllowUserToDeleteRows = false,
ReadOnly = true, // Başlangıçta ReadOnly
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells,
BackgroundColor = Color.LightGray,
BorderStyle = BorderStyle.Fixed3D,
RowHeadersVisible = true, // Silme/Düzenleme için RowHeader lazım olabilir
SelectionMode = DataGridViewSelectionMode.FullRowSelect, // Satır bazlı seçim
MultiSelect = true // Çoklu satır silme için
};
SetupDataGridViewStyles(dgvResults);
pageQueryResults.Controls.Add(dgvResults);
// Sil Butonu (Sonuçlar sekmesine)
btnDeleteSelectedRows = new Button { Text = "Seçili Satırları Sil", Dock = DockStyle.Bottom, Height = 30, BackColor = Color.OrangeRed, ForeColor = Color.White, Font = new Font(this.Font, FontStyle.Bold), Enabled = false, Visible = false }; // Başlangıçta gizli ve pasif
pageQueryResults.Controls.Add(btnDeleteSelectedRows);
btnDeleteSelectedRows.Click += BtnDeleteSelectedRows_Click;
// Sekme 2: Tablo Yapısı
pageTableStructure = new TabPage("Tablo Yapısı");
tabResults.TabPages.Add(pageTableStructure);
dgvStructure = new DataGridView { Dock = DockStyle.Fill, AllowUserToAddRows = false, AllowUserToDeleteRows = false, ReadOnly = true, AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells, BackgroundColor = Color.White, BorderStyle = BorderStyle.Fixed3D, RowHeadersVisible = false };
SetupDataGridViewStyles(dgvStructure);
pageTableStructure.Controls.Add(dgvStructure);
#endregion
// Olay Yöneticilerini Bağla
btnConnect.Click += BtnConnect_Click;
btnDisconnect.Click += BtnDisconnect_Click;
cmbDatabases.SelectedIndexChanged += CmbDatabases_SelectedIndexChanged;
cmbTables.SelectedIndexChanged += CmbTables_SelectedIndexChanged;
btnExecuteSql.Click += BtnExecuteSql_Click;
dgvResults.CellValueChanged += DgvResults_CellValueChanged; // Hücre değeri değişince
dgvResults.SelectionChanged += DgvResults_SelectionChanged; // Seçim değişince (Sil butonunu aktif/pasif yapmak için)
}
private void SetupDataGridViewStyles(DataGridView dgv)
{
dgv.ColumnHeadersDefaultCellStyle.BackColor = Color.Navy;
dgv.ColumnHeadersDefaultCellStyle.ForeColor = Color.White;
dgv.ColumnHeadersDefaultCellStyle.Font = new Font(dgv.Font, FontStyle.Bold);
dgv.AlternatingRowsDefaultCellStyle.BackColor = Color.Gainsboro;
dgv.DefaultCellStyle.SelectionBackColor = Color.DarkTurquoise;
dgv.DefaultCellStyle.SelectionForeColor = Color.WhiteSmoke;
dgv.BackgroundColor = Color.FromArgb(220, 220, 220); // Biraz daha açık gri
dgv.GridColor = Color.DarkGray;
}
// --- Olay Yöneticileri (Event Handlers) ---
private async void BtnConnect_Click(object sender, EventArgs e)
{
string server = txtServer.Text.Trim();
string userId = txtUserId.Text.Trim();
string password = txtPassword.Text;
if (string.IsNullOrEmpty(server) || string.IsNullOrEmpty(userId))
{
MessageBox.Show("Server ve User ID alanları boş bırakılamaz.", "Uyarı", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
await DisconnectAsync(); // Önce mevcut bağlantıyı kapat
currentConnectionString = $"Server={server};Uid={userId};Pwd={password};Connection Timeout=15;Allow User Variables=True;"; // Allow User Variables gerekebilir
connection = new MySqlConnection(currentConnectionString);
SetStatus("Bağlanılıyor...", true);
try
{
await connection.OpenAsync(); // Async bağlantı
SetStatus($"Bağlandı: {server}", false);
SaveLastConnectionInfo(server, userId); // Başarılı bağlantıyı kaydet
UpdateConnectionState(true);
await PopulateDatabasesAsync(); // Async DB listeleme
}
catch (MySqlException ex)
{
SetStatus("Bağlantı Başarısız", false);
MessageBox.Show($"Bağlantı hatası: {ex.Message} (Hata Kodu: {ex.Number})", "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error);
await DisconnectAsync(); // Hata durumunda temizle
}
catch (TimeoutException ex)
{
SetStatus("Bağlantı Zaman Aşımı", false);
MessageBox.Show($"Bağlantı zaman aşımına uğradı: {ex.Message}\nSunucu adresini ve ağ bağlantınızı kontrol edin.", "Zaman Aşımı", MessageBoxButtons.OK, MessageBoxIcon.Warning);
await DisconnectAsync();
}
catch (Exception ex)
{
SetStatus("Hata Oluştu", false);
MessageBox.Show($"Beklenmedik bir hata oluştu: {ex.Message}", "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error);
await DisconnectAsync();
}
}
private async void BtnDisconnect_Click(object sender, EventArgs e)
{
await DisconnectAsync();
}
private async void CmbDatabases_SelectedIndexChanged(object sender, EventArgs e)
{
if (cmbDatabases.SelectedIndex >= 0 && connection?.State == ConnectionState.Open)
{
string selectedDb = cmbDatabases.SelectedItem.ToString();
SetStatus($"Veritabanı '{selectedDb}' seçiliyor...", true);
try
{
// ChangeDatabase async değil, sorun olabilir ama genellikle hızlıdır.
// Daha güvenli yol: yeni bağlantı açmak veya komutlara `USE db;` eklemek.
// Şimdilik ChangeDatabase ile devam edelim.
connection.ChangeDatabase(selectedDb);
SetStatus($"Bağlı: {connection.DataSource} -> {selectedDb}", false);
UpdateUiForNewDatabaseSelection(); // Tabloları vs. temizle/hazırla
await PopulateTablesAsync(selectedDb);
}
catch (MySqlException ex)
{
SetStatus($"Hata: DB değiştirilemedi", false);
MessageBox.Show($"Veritabanı değiştirilemedi: {ex.Message}", "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error);
UpdateUiForNewDatabaseSelection(error: true);
}
catch (Exception ex)
{
SetStatus($"Hata: DB değiştirilemedi", false);
MessageBox.Show($"Veritabanı değiştirilirken genel hata: {ex.Message}", "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error);
UpdateUiForNewDatabaseSelection(error: true);
}
}
else
{
UpdateUiForNewDatabaseSelection(clearOnly: true);
if (connection?.State == ConnectionState.Open)
SetStatus($"Bağlı: {connection.DataSource}", false);
else
SetStatus("Bağlı Değil", false);
}
}
private async void CmbTables_SelectedIndexChanged(object sender, EventArgs e)
{
dgvResults.DataSource = null;
dgvStructure.DataSource = null;
currentlyDisplayedTable = "";
primaryKeyColumns.Clear();
SetEditingState(false); // Yeni tablo seçildi, düzenleme kapalı
if (cmbTables.SelectedIndex >= 0 && connection?.State == ConnectionState.Open)
{
string selectedTable = cmbTables.SelectedItem.ToString();
if (!selectedTable.StartsWith("(")) // Gerçek bir tablo adıysa
{
currentlyDisplayedTable = selectedTable;
// Tablo seçildiğinde otomatik olarak yapıyı ve veriyi yükleyebiliriz
// Veya sağ tık menüsüyle kullanıcıya bırakabiliriz. Otomatik yükleyelim:
await ShowTableStructureAsync(selectedTable); // Önce yapıyı al (PK lazım)
await ShowTableDataAsync(selectedTable); // Sonra veriyi
tabResults.SelectedTab = pageQueryResults; // Sonuçlar sekmesini göster
}
}
}
private async void BtnExecuteSql_Click(object sender, EventArgs e)
{
string sql = txtSqlEditor.SelectedText.Trim();
if (string.IsNullOrEmpty(sql))
{
sql = txtSqlEditor.Text.Trim();
}
if (!ValidateExecutionReady(sql)) return;
SetStatus("Çalıştırılıyor...", true);
try
{
string commandTypeCheck = sql.TrimStart().Split(' ')[0].ToUpperInvariant();
if (commandTypeCheck == "SELECT" || commandTypeCheck == "SHOW" ||
commandTypeCheck == "DESC" || commandTypeCheck == "DESCRIBE" ||
commandTypeCheck == "EXPLAIN")
{
await ExecuteQueryAsync(sql);
tabResults.SelectedTab = pageQueryResults; // Sonuç sekmesini göster
}
else
{
await ExecuteNonQueryAsync(sql);
// Non-query sonrası hangi sekmeyi göstereceğimiz duruma bağlı, şimdilik değişmesin.
}
}
catch (Exception ex) // Execute metotlarındaki hatalar burada da yakalanabilir (genel)
{
SetStatus("Çalıştırma Hatası!", false);
MessageBox.Show($"Komut çalıştırılırken hata: {ex.Message}", "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
SetStatus("", false); // Durumu temizle veya sonuca göre güncelle (Execute metotları içinde yapılıyor)
}
}
// Hücre değeri değiştiğinde UPDATE sorgusu gönderir
private async void DgvResults_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex < 0 || e.ColumnIndex < 0) return; // Header veya geçersiz hücre
if (!dgvResults.ReadOnly) // Sadece düzenleme modu aktifse
{
if (primaryKeyColumns.Count == 0)
{
MessageBox.Show("Bu tablonun birincil anahtarı bulunamadığı için değişiklik kaydedilemiyor.", "Uyarı", MessageBoxButtons.OK, MessageBoxIcon.Warning);
// Değişikliği geri almayı deneyebiliriz ama DataGridView bunu zorlaştırır. Veriyi yenilemek daha garanti.
await ShowTableDataAsync(currentlyDisplayedTable);
return;
}
DataGridViewRow changedRow = dgvResults.Rows[e.RowIndex];
string changedColumnName = dgvResults.Columns[e.ColumnIndex].Name;
object newValue = changedRow.Cells[e.ColumnIndex].Value;
// PK değer(ler)ini al
Dictionary<string, object> pkValues = new Dictionary<string, object>();
foreach (string pkCol in primaryKeyColumns)
{
if (changedRow.Cells[pkCol] != null)
{ // Hücre var mı?
pkValues[pkCol] = changedRow.Cells[pkCol].Value;
}
else
{
MessageBox.Show($"Birincil anahtar sütunu '{pkCol}' grid'de bulunamadı!", "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error);
await ShowTableDataAsync(currentlyDisplayedTable); // Yenile
return;
}
}
if (pkValues.Count != primaryKeyColumns.Count)
{
MessageBox.Show("Tüm birincil anahtar değerleri alınamadı.", "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error);
await ShowTableDataAsync(currentlyDisplayedTable); // Yenile
return;
}
// UPDATE sorgusunu oluştur (PARAMETRELİ!)
string setClause = $"`{changedColumnName}` = @newValue";
string whereClause = string.Join(" AND ", primaryKeyColumns.Select(pk => $"`{pk}` = @{pk}"));
string updateSql = $"UPDATE `{currentlyDisplayedTable}` SET {setClause} WHERE {whereClause}";
SetStatus("Değişiklik kaydediliyor...", true);
using (MySqlCommand cmd = new MySqlCommand(updateSql, connection))
{
cmd.Parameters.AddWithValue("@newValue", newValue ?? DBNull.Value); // Null değerler için DBNull
foreach (var kvp in pkValues)
{
cmd.Parameters.AddWithValue($"@{kvp.Key}", kvp.Value ?? DBNull.Value);
}
try
{
int affectedRows = await cmd.ExecuteNonQueryAsync();
if (affectedRows > 0)
{
SetStatus($"Değişiklik kaydedildi ({affectedRows} satır).", false);
// İsteğe bağlı: Satırın rengini değiştirip sonra normale döndürerek görsel geri bildirim
changedRow.DefaultCellStyle.BackColor = Color.LightGreen;
await Task.Delay(500); // Kısa bir süre bekle
dgvResults.InvalidateRow(e.RowIndex); // Rengi sıfırla (veya AlternatingRows rengine döner)
}
else
{
SetStatus("Değişiklik kaydedildi (0 satır etkilendi - PK değişmiş olabilir?).", false);
MessageBox.Show("Değişiklik yapıldı ancak veritabanında eşleşen satır bulunamadı veya değer aynıydı.", "Uyarı", MessageBoxButtons.OK, MessageBoxIcon.Warning);
// Veriyi yenilemek en sağlıklısı olabilir
await ShowTableDataAsync(currentlyDisplayedTable);
}
}
catch (MySqlException ex)
{
SetStatus("Kayıt Hatası!", false);
MessageBox.Show($"Değişiklik kaydedilemedi: {ex.Message}", "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error);
// Hata sonrası veriyi yenilemek tutarlılık için önemli
await ShowTableDataAsync(currentlyDisplayedTable);
}
catch (Exception ex)
{
SetStatus("Kayıt Hatası!", false);
MessageBox.Show($"Değişiklik kaydedilirken genel hata: {ex.Message}", "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error);
await ShowTableDataAsync(currentlyDisplayedTable);
}
}
}
}
// Seçili satırları siler
private async void BtnDeleteSelectedRows_Click(object sender, EventArgs e)
{
if (dgvResults.SelectedRows.Count == 0)
{
MessageBox.Show("Lütfen silmek için en az bir satır seçin.", "Uyarı", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
if (primaryKeyColumns.Count == 0)
{
MessageBox.Show("Bu tablonun birincil anahtarı bulunamadığı için silme işlemi yapılamıyor.", "Uyarı", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
string tableName = currentlyDisplayedTable; // Silinecek tablo
int rowCount = dgvResults.SelectedRows.Count;
var confirmation = MessageBox.Show($"{rowCount} adet satır '{tableName}' tablosundan kalıcı olarak silinecek!\n\nBu işlem geri alınamaz! Emin misiniz?",
"Silme Onayı", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2);
if (confirmation == DialogResult.Yes)
{
SetStatus("Satırlar siliniyor...", true);
int successCount = 0;
int failCount = 0;
List<DataGridViewRow> rowsToDeleteFromGrid = new List<DataGridViewRow>();
// Her satır için ayrı DELETE komutu (toplu işlem için transaction gerekebilir)
foreach (DataGridViewRow row in dgvResults.SelectedRows)
{
if (row.IsNewRow) continue; // Yeni eklenen (ama kaydedilmemiş) satırı atla
Dictionary<string, object> pkValues = new Dictionary<string, object>();
bool pkOk = true;
foreach (string pkCol in primaryKeyColumns)
{
if (row.Cells[pkCol] != null)
{
pkValues[pkCol] = row.Cells[pkCol].Value;
}
else
{
pkOk = false;
break;
}
}
if (!pkOk || pkValues.Count != primaryKeyColumns.Count)
{
failCount++;
Debug.WriteLine($"PK alınamadı, satır atlandı: {row.Index}");
continue; // Bu satır için PK alınamadı, atla
}
string whereClause = string.Join(" AND ", primaryKeyColumns.Select(pk => $"`{pk}` = @{pk}"));
string deleteSql = $"DELETE FROM `{tableName}` WHERE {whereClause}";
using (MySqlCommand cmd = new MySqlCommand(deleteSql, connection))
{
foreach (var kvp in pkValues)
{
cmd.Parameters.AddWithValue($"@{kvp.Key}", kvp.Value ?? DBNull.Value);
}
try
{
int affected = await cmd.ExecuteNonQueryAsync();
if (affected > 0)
{
successCount++;
rowsToDeleteFromGrid.Add(row); // Başarılıysa grid'den silinecekler listesine ekle
}
else
{
failCount++; // Eşleşen satır bulunamadı
Debug.WriteLine($"Satır bulunamadı veya silinemedi: PKs={string.Join(",", pkValues.Values)}");
}
}
catch (Exception ex)
{
failCount++;
Debug.WriteLine($"Silme hatası: {ex.Message} - PKs={string.Join(",", pkValues.Values)}");
// Tek bir satır silinemezse bile devam etmeyi deneyebiliriz.
// Ya da ilk hatada durabiliriz. Şimdilik devam edelim.
}
}
} // End foreach row
// Başarılı olanları grid'den kaldır
foreach (var row in rowsToDeleteFromGrid)
{
// Eğer DataGridView bir DataTable'a bağlıysa, Rows.Remove yerine
// DataTable'dan satırı silip grid'i yenilemek daha doğru olabilir.
// Şimdilik doğrudan grid'den silelim.
if (!row.IsNewRow && row.DataGridView != null) // Hala grid'de mi kontrolü
{
try { dgvResults.Rows.Remove(row); } catch (Exception removeEx) { Debug.WriteLine($"Grid'den satır silme hatası: {removeEx.Message}"); }
}
}
SetStatus($"{successCount} satır silindi, {failCount} hata.", false);
MessageBox.Show($"{successCount} satır başarıyla silindi.\n{failCount} satır silinemedi (hata veya bulunamama).", "Sonuç", MessageBoxButtons.OK, failCount > 0 ? MessageBoxIcon.Warning : MessageBoxIcon.Information);
// Alternatif olarak, silme sonrası tüm veriyi yenileyebiliriz:
// await ShowTableDataAsync(tableName);
}
}
// Sağ tık menüsü: Veritabanını Sil
private async void ContextMenuDbDelete_Click(object sender, EventArgs e)
{
if (cmbDatabases.SelectedItem == null) return;
string dbName = cmbDatabases.SelectedItem.ToString();
var confirmation = MessageBox.Show($"'{dbName}' veritabanı ve İÇİNDEKİ TÜM TABLOLAR kalıcı olarak silinecek!\n\nBU İŞLEM GERİ ALINAMAZ!\n\nEMİN MİSİNİZ???",
"!!! VERİTABANI SİLME ONAYI !!!", MessageBoxButtons.YesNo, MessageBoxIcon.Error, MessageBoxDefaultButton.Button2);
if (confirmation == DialogResult.Yes)
{
// Ekstra bir güvenlik katmanı olarak kullanıcıdan DB adını tekrar yazmasını isteyebiliriz.
// SimplePrompt dialog = new SimplePrompt($"Silmek istediğiniz veritabanının adını ('{dbName}') aşağıdaki kutuya yazın:", "Son Onay");
// if (dialog.ShowDialog() == DialogResult.OK && dialog.InputText == dbName) { ... }
string sql = $"DROP DATABASE `{dbName}`";
SetStatus($"'{dbName}' siliniyor...", true);
await ExecuteNonQueryAsync(sql, true); // true: başarı sonrası listeleri yenile
SetStatus($"'{dbName}' silindi.", false); // ExecuteNonQueryAsync durumu günceller ama biz tekrar yazalım
}
}
// Sağ tık menüsü: Tabloyu Sil
private async void ContextMenuTableDelete_Click(object sender, EventArgs e)
{
if (cmbTables.SelectedItem == null) return;
string tableName = cmbTables.SelectedItem.ToString();
if (tableName.StartsWith("(")) return; // Gerçek tablo adı değilse çık
var confirmation = MessageBox.Show($"'{tableName}' tablosu kalıcı olarak silinecek!\n\nBu işlem geri alınamaz! Emin misiniz?",
"Tablo Silme Onayı", MessageBoxButtons.YesNo, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button2);
if (confirmation == DialogResult.Yes)
{
string sql = $"DROP TABLE `{tableName}`";
SetStatus($"'{tableName}' siliniyor...", true);
await ExecuteNonQueryAsync(sql, true); // true: başarı sonrası listeleri yenile
SetStatus($"'{tableName}' silindi.", false);
}
}
// Sağ tık menüsü: Tablo Verisini Göster
private async void ContextMenuTableShowData_Click(object sender, EventArgs e)
{
if (cmbTables.SelectedItem == null) return;
string tableName = cmbTables.SelectedItem.ToString();
if (tableName.StartsWith("(")) return;
await ShowTableDataAsync(tableName);
tabResults.SelectedTab = pageQueryResults; // Sonuçlar sekmesini aç
}
// Sağ tık menüsü: Tablo Yapısını Göster
private async void ContextMenuTableShowStructure_Click(object sender, EventArgs e)
{
if (cmbTables.SelectedItem == null) return;
string tableName = cmbTables.SelectedItem.ToString();
if (tableName.StartsWith("(")) return;
await ShowTableStructureAsync(tableName);
tabResults.SelectedTab = pageTableStructure; // Yapı sekmesini aç
}
// F5'e basınca sorguyu çalıştırma
private void TxtSqlEditor_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.F5)
{
BtnExecuteSql_Click(sender, EventArgs.Empty); // Execute butonunun click olayını tetikle
e.SuppressKeyPress = true; // F5'in başka bir işlev yapmasını engelle
}
}
// Grid seçimi değiştiğinde Sil butonunu ve düzenleme durumunu ayarla
private void DgvResults_SelectionChanged(object sender, EventArgs e)
{
bool canDelete = dgvResults.SelectedRows.Count > 0 && !dgvResults.ReadOnly && primaryKeyColumns.Count > 0;
btnDeleteSelectedRows.Enabled = canDelete;
}
// Form kapanırken
private async void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
await DisconnectAsync();
}
// Form yüklenirken
private void Form1_Load(object sender, EventArgs e)
{
LoadLastConnectionInfo(); // Kayıtlı bağlantı bilgilerini yükle
}
// --- Yardımcı Metotlar ---
// Bağlantı durumuna göre UI elemanlarını günceller
private void UpdateConnectionState(bool isConnected)
{
txtServer.Enabled = !isConnected;
txtUserId.Enabled = !isConnected;
txtPassword.Enabled = !isConnected;
btnConnect.Enabled = !isConnected;
btnDisconnect.Enabled = isConnected;
cmbDatabases.Enabled = isConnected;
// Diğer kontrollerin durumu ayrıca DB/Tablo seçimine bağlı olacak
if (!isConnected)
{
cmbDatabases.DataSource = null; cmbDatabases.Items.Clear();
cmbTables.DataSource = null; cmbTables.Items.Clear(); cmbTables.Enabled = false;
txtSqlEditor.Clear(); txtSqlEditor.Enabled = false;
btnExecuteSql.Enabled = false;
dgvResults.DataSource = null; dgvStructure.DataSource = null;
tabResults.SelectedTab = pageQueryResults; // Başlangıç sekmesi
SetEditingState(false); // Düzenleme kapalı
currentlyDisplayedTable = "";
primaryKeyColumns.Clear();
}
// Sağ tık menülerini bağlantı durumuna göre etkinleştir/devre dışı bırak
contextMenuDbList.Enabled = isConnected;
contextMenuTableList.Enabled = isConnected;
}
// Yeni DB seçildiğinde veya bağlantı koptuğunda UI'ı temizler/ayarlar
private void UpdateUiForNewDatabaseSelection(bool error = false, bool clearOnly = false)
{
cmbTables.DataSource = null; cmbTables.Items.Clear(); cmbTables.Enabled = !error && !clearOnly;
txtSqlEditor.Enabled = !error && !clearOnly;
btnExecuteSql.Enabled = !error && !clearOnly;
dgvResults.DataSource = null;
dgvStructure.DataSource = null;
currentlyDisplayedTable = "";
primaryKeyColumns.Clear();
SetEditingState(false); // Düzenleme kapalı
tabResults.SelectedTab = pageQueryResults;
if (error || clearOnly)
{
cmbTables.Enabled = false;
txtSqlEditor.Enabled = false;
btnExecuteSql.Enabled = false;
}
}
// Asenkron bağlantı kesme
private async Task DisconnectAsync()
{
if (connection != null)
{
SetStatus("Bağlantı kesiliyor...", false); // Meşgul göstermeden
try
{
if (connection.State == ConnectionState.Open)
{
// Normalde Close() sync ama hızlı olmalı. Async versiyonu yok.
connection.Close();
}
// Dispose async değil ama awaitable yapmak için Task.Run içine alınabilir (genelde gereksiz)
await Task.Run(() => connection.Dispose());
}
catch (Exception ex)
{
Debug.WriteLine($"Disconnect error: {ex.Message}");
}
finally
{
connection = null;
currentConnectionString = "";
}
}
UpdateConnectionState(false); // UI'ı güncelle
SetStatus("Bağlı Değil", false);
}
// Asenkron veritabanı listeleme
private async Task PopulateDatabasesAsync()
{
SetStatus("Veritabanları listeleniyor...", true);
try
{
DataTable databases = new DataTable();
using (MySqlCommand cmd = new MySqlCommand("SHOW DATABASES;", connection))
using (MySqlDataAdapter adapter = new MySqlDataAdapter(cmd))
{
// Fill async değil. Büyük DB listeleri için Task.Run gerekebilir.
await Task.Run(() => adapter.Fill(databases));
}
cmbDatabases.DataSource = null; cmbDatabases.Items.Clear();
List<string> dbList = new List<string>();
foreach (DataRow row in databases.Rows) { dbList.Add(row["Database"].ToString()); }
dbList.Sort();
cmbDatabases.Items.AddRange(dbList.ToArray());
cmbDatabases.SelectedIndex = -1;
cmbDatabases.Enabled = true;
SetStatus($"Veritabanları listelendi.", false);
}
catch (Exception ex)
{
SetStatus("Hata: DB listelenemedi", false);
MessageBox.Show($"Veritabanları listelenirken hata: {ex.Message}", "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error);
cmbDatabases.Enabled = false;
}
}
// Asenkron tablo listeleme
private async Task PopulateTablesAsync(string databaseName)
{
if (string.IsNullOrEmpty(databaseName)) return;
SetStatus($"'{databaseName}' tabloları listeleniyor...", true);
try
{
DataTable tables = new DataTable();
using (MySqlCommand cmd = new MySqlCommand($"SHOW TABLES;", connection))
using (MySqlDataAdapter adapter = new MySqlDataAdapter(cmd))
{
await Task.Run(() => adapter.Fill(tables));
}
cmbTables.DataSource = null; cmbTables.Items.Clear();
if (tables.Rows.Count > 0)
{
string columnName = tables.Columns[0].ColumnName;
List<string> tableList = new List<string>();
foreach (DataRow row in tables.Rows) { tableList.Add(row[columnName].ToString()); }
tableList.Sort();
cmbTables.Items.AddRange(tableList.ToArray());
cmbTables.SelectedIndex = -1;
}
else { cmbTables.Items.Add("(Tablo Yok)"); cmbTables.SelectedIndex = 0; }
cmbTables.Enabled = true;
SetStatus($"'{databaseName}' tabloları listelendi.", false);
}
catch (Exception ex)
{
SetStatus("Hata: Tablolar listelenemedi", false);
MessageBox.Show($"'{databaseName}' veritabanındaki tablolar listelenirken hata: {ex.Message}", "Hata", MessageBoxButtons.OK, MessageBoxIcon.Error);
cmbTables.Enabled = false;
}
}
// Asenkron sorgu çalıştırma (SELECT vb.)
private async Task ExecuteQueryAsync(string query)
{
Stopwatch stopwatch = Stopwatch.StartNew();
SetStatus("Sorgu çalıştırılıyor...", true);
DataTable dt = new DataTable();
currentlyDisplayedTable = ""; // Bu bir sorgu sonucu, belirli bir tablo değil
primaryKeyColumns.Clear();
SetEditingState(false); // Sorgu sonuçları düzenlenemez
try
{
using (MySqlCommand cmd = new MySqlCommand(query, connection))
{
cmd.CommandTimeout = 120;
using (var reader = await cmd.ExecuteReaderAsync()) // Async reader
{
dt.Load(reader); // Reader'dan DataTable'a yükle
}
}
stopwatch.Stop();
dgvResults.DataSource = dt;
AdjustDataGridViewColumns(dgvResults); // Sütunları ayarla
SetStatus($"{dt.Rows.Count} satır döndürüldü ({stopwatch.Elapsed.TotalSeconds:F2} s).", false);
}
catch (Exception ex)
{
stopwatch.Stop();
SetStatus($"Sorgu Hatası! ({stopwatch.Elapsed.TotalSeconds:F2} s)", false);
MessageBox.Show($"SQL Sorgu Hatası: {ex.Message}\n\nSorgu: {query}", "Sorgu Hatası", MessageBoxButtons.OK, MessageBoxIcon.Error);
dgvResults.DataSource = null;
}
}
// Asenkron komut çalıştırma (INSERT, UPDATE, DELETE, DROP vb.)
// refreshLists: Başarı durumunda DB/Table listelerini yenilesin mi?
private async Task ExecuteNonQueryAsync(string command, bool refreshLists = false)
{
Stopwatch stopwatch = Stopwatch.StartNew();
SetStatus("Komut çalıştırılıyor...", true);
string originalDb = cmbDatabases.SelectedItem?.ToString(); // Komut öncesi durum
try
{
int affectedRows = 0;
using (MySqlCommand cmd = new MySqlCommand(command, connection))
{
cmd.CommandTimeout = 120;
affectedRows = await cmd.ExecuteNonQueryAsync(); // Async çalıştırma
}
stopwatch.Stop();
SetStatus($"Komut başarıyla çalıştırıldı. Etkilenen satır: {affectedRows} ({stopwatch.Elapsed.TotalSeconds:F2} s).", false);
dgvResults.DataSource = null; // NonQuery sonrası gridi temizle
dgvStructure.DataSource = null; // Yapı gridini de temizle
if (refreshLists)
{
await PopulateDatabasesAsync();
if (!string.IsNullOrEmpty(originalDb) && cmbDatabases.Items.Contains(originalDb))
{
cmbDatabases.SelectedItem = originalDb; // Bu, tabloları da yenilemeli (event tetiklenir)
}
else
{
// Eğer eski DB silindiyse veya seçilemiyorsa UI temizlenir
UpdateUiForNewDatabaseSelection(clearOnly: true);
if (connection?.State == ConnectionState.Open)
SetStatus($"Bağlı: {connection.DataSource} (Listeler Yenilendi)", false);
}
}
}
catch (Exception ex)
{
stopwatch.Stop();
SetStatus($"Komut Hatası! ({stopwatch.Elapsed.TotalSeconds:F2} s)", false);
MessageBox.Show($"SQL Komut Hatası: {ex.Message}\n\nKomut: {command}", "Komut Hatası", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
// Tablo verisini DataGridView'de gösterir
private async Task ShowTableDataAsync(string tableName)
{
if (string.IsNullOrEmpty(tableName) || connection?.State != ConnectionState.Open) return;
string query = $"SELECT * FROM `{tableName}`"; // LIMIT ekleyebiliriz? LIMIT 500;
Stopwatch stopwatch = Stopwatch.StartNew();
SetStatus($"'{tableName}' verisi yükleniyor...", true);
DataTable dt = new DataTable();
try
{
using (MySqlCommand cmd = new MySqlCommand(query, connection))
{
cmd.CommandTimeout = 120;
using (var reader = await cmd.ExecuteReaderAsync())
{
dt.Load(reader);
}
}
stopwatch.Stop();
currentlyDisplayedTable = tableName; // Şu an bu tablo gösteriliyor
dgvResults.DataSource = dt;
AdjustDataGridViewColumns(dgvResults);
// Düzenleme durumunu ayarla (PK varsa)
SetEditingState(primaryKeyColumns.Count > 0);
SetStatus($"'{tableName}' tablosu: {dt.Rows.Count} satır ({stopwatch.Elapsed.TotalSeconds:F2} s). " + (dgvResults.ReadOnly ? "(Salt Okunur)" : "(Düzenlenebilir)"), false);
}
catch (Exception ex)
{
stopwatch.Stop();
SetStatus($"'{tableName}' verisi yüklenemedi!", false);
MessageBox.Show($"'{tableName}' tablosu yüklenirken hata: {ex.Message}", "Veri Yükleme Hatası", MessageBoxButtons.OK, MessageBoxIcon.Error);
dgvResults.DataSource = null;
SetEditingState(false);
currentlyDisplayedTable = "";
}
}
// Tablo yapısını DataGridView'de gösterir ve PK'ları bulur
private async Task ShowTableStructureAsync(string tableName)
{
if (string.IsNullOrEmpty(tableName) || connection?.State != ConnectionState.Open) return;
primaryKeyColumns.Clear(); // Önceki PK'ları temizle
string query = $"DESCRIBE `{tableName}`"; // veya SHOW COLUMNS FROM ...
Stopwatch stopwatch = Stopwatch.StartNew();
SetStatus($"'{tableName}' yapısı alınıyor...", true);
DataTable dtStructure = new DataTable();
try
{
using (MySqlCommand cmd = new MySqlCommand(query, connection))
using (MySqlDataAdapter adapter = new MySqlDataAdapter(cmd))
{
await Task.Run(() => adapter.Fill(dtStructure));
}
// PK'ları bulmak için SHOW KEYS kullanalım (DESCRIBE'da da var ama bu daha net)
DataTable dtKeys = new DataTable();
using (MySqlCommand cmdKeys = new MySqlCommand($"SHOW KEYS FROM `{tableName}` WHERE Key_name = 'PRIMARY'", connection))
using (MySqlDataAdapter adapterKeys = new MySqlDataAdapter(cmdKeys))
{
await Task.Run(() => adapterKeys.Fill(dtKeys));
}
foreach (DataRow row in dtKeys.Rows)
{
primaryKeyColumns.Add(row["Column_name"].ToString());
}
stopwatch.Stop();
dgvStructure.DataSource = dtStructure;
AdjustDataGridViewColumns(dgvStructure);
SetStatus($"'{tableName}' yapısı alındı ({stopwatch.Elapsed.TotalSeconds:F2} s). " + (primaryKeyColumns.Count > 0 ? $"PK: {string.Join(", ", primaryKeyColumns)}" : "PK Yok"), false);
}
catch (Exception ex)
{
stopwatch.Stop();
SetStatus($"'{tableName}' yapısı alınamadı!", false);
MessageBox.Show($"'{tableName}' tablo yapısı alınırken hata: {ex.Message}", "Yapı Alma Hatası", MessageBoxButtons.OK, MessageBoxIcon.Error);
dgvStructure.DataSource = null;
primaryKeyColumns.Clear();
}
}
// Durum çubuğunu ve imleci günceller
private void SetStatus(string message, bool busy)
{
if (lblStatus.InvokeRequired)
{
lblStatus.Invoke((MethodInvoker)delegate { SetStatus(message, busy); });
}
else
{
if (!string.IsNullOrEmpty(message)) lblStatus.Text = message;
this.Cursor = busy ? Cursors.WaitCursor : Cursors.Default;
// İsteğe bağlı: StatusStrip ve ProgressBar eklenebilir.
}
}
// DataGridView sütunlarını ayarlar (Görüntüleme için)
private void AdjustDataGridViewColumns(DataGridView dgv)
{
if (dgv.DataSource == null) return;
// Çok fazla sütun varsa otomatik boyutlandırmayı kapat
bool manyColumns = dgv.ColumnCount > 25;
dgv.AutoSizeColumnsMode = manyColumns ? DataGridViewAutoSizeColumnsMode.None : DataGridViewAutoSizeColumnsMode.DisplayedCells;
// Çok fazla satır varsa performansı etkileyebilir, dikkatli kullanılmalı
// dgv.AutoResizeRowsMode = DataGridViewAutoSizeRowsMode.DisplayedCells;
}
// DataGridView düzenleme modunu ve Sil butonunu ayarlar
private void SetEditingState(bool canEdit)
{
dgvResults.ReadOnly = !canEdit;
btnDeleteSelectedRows.Visible = canEdit; // Sil butonu sadece düzenlenebilirken görünsün
btnDeleteSelectedRows.Enabled = canEdit && dgvResults.SelectedRows.Count > 0; // Seçim varsa aktif
// İsteğe bağlı: ReadOnly değişince hücre renklerini ayarlayabiliriz
dgvResults.DefaultCellStyle.BackColor = canEdit ? Color.White : SystemColors.Control; // Düzenlenemezse gri yap
dgvResults.AlternatingRowsDefaultCellStyle.BackColor = canEdit ? Color.Gainsboro : SystemColors.ControlLight;
}
// Çalıştırma için kontroller (Bağlantı, Seçili DB)
private bool ValidateExecutionReady(string sql)
{
if (string.IsNullOrEmpty(sql))
{
MessageBox.Show("Lütfen çalıştırılacak bir SQL komutu girin veya seçin.", "Uyarı", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return false;
}
if (connection == null || connection.State != ConnectionState.Open)
{
MessageBox.Show("Lütfen önce veritabanına bağlanın.", "Uyarı", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return false;
}
if (cmbDatabases.SelectedIndex < 0 && !sql.TrimStart().StartsWith("CREATE DATABASE", StringComparison.OrdinalIgnoreCase) && !sql.TrimStart().StartsWith("SHOW DATABASES", StringComparison.OrdinalIgnoreCase))
{
// Veritabanı gerektirmeyen bazı komutlar hariç DB seçimi istenir
MessageBox.Show("Lütfen işlem yapmak için bir veritabanı seçin.", "Uyarı", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return false;
}
return true;
}
// --- Bağlantı Bilgilerini Kaydetme/Yükleme ---
private class ConnectionInfo
{
public string Server { get; set; }
public string UserId { get; set; }
}
private void SaveLastConnectionInfo(string server, string userId)
{
try
{
var info = new ConnectionInfo { Server = server, UserId = userId };
string json = JsonSerializer.Serialize(info);
File.WriteAllText(SettingsFileName, json);
}
catch (Exception ex)
{
Debug.WriteLine($"Ayarları kaydetme hatası: {ex.Message}");
}
}
private void LoadLastConnectionInfo()
{
try
{
if (File.Exists(SettingsFileName))
{
string json = File.ReadAllText(SettingsFileName);
var info = JsonSerializer.Deserialize<ConnectionInfo>(json);
if (info != null)
{
txtServer.Text = info.Server;
txtUserId.Text = info.UserId;
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"Ayarları yükleme hatası: {ex.Message}");
// Hata durumunda dosyayı silmeyi düşünebiliriz
try { File.Delete(SettingsFileName); } catch { }
}
// Şifre alanı güvenlik nedeniyle her zaman boş başlasın
txtPassword.Text = "";
}
}
}