C#/.NET Windows Forms书店数据库应用开发(含事件处理)技术求助
Hey there! It sounds like you've already made solid progress on your C#/.NET WinForms bookstore assignment—let's dive into the most common bottlenecks folks hit with this kind of app, and break down practical fixes for each key feature:
1. Admin/Regular User Permission Separation (Non-Simultaneous Login)
First, let's lock down the user role system—this is usually where folks get tripped up on access control:
- Database Setup: Add a
Userstable with fields likeUserId,Username,PasswordHash(never store plaintext passwords!), andRole(e.g.,"Admin"or"Regular"). UseBCrypt.Net-NextorSystem.Security.Cryptographyto hash passwords during user creation. - Persist Role After Login: Store the authenticated user's role in a static property in your
Programclass, likepublic static string CurrentUserRole { get; set; }. This lets you access the role across all forms without passing it around manually. - Control UI Access: On form load, check the role and toggle control visibility/enability:
private void MainDashboard_Load(object sender, EventArgs e) { if (Program.CurrentUserRole == "Regular") { // Hide admin-only features btnPurchaseInventory.Visible = false; btnGenerateSalesReport.Enabled = false; } }
2. Database-Driven Core Features
Adding Customers (Safe & Clean)
Always use parameterized queries to avoid SQL injection—here's a quick implementation for your add customer button:
private async void btnAddCustomer_Click(object sender, EventArgs e) { var connectionString = @"Your_Connection_String_Here"; // Replace with your actual string using var conn = new SqlConnection(connectionString); await conn.OpenAsync(); var query = @"INSERT INTO Customers (FullName, Email, PhoneNumber) VALUES (@FullName, @Email, @PhoneNumber)"; using var cmd = new SqlCommand(query, conn); // Add parameters to prevent injection cmd.Parameters.AddWithValue("@FullName", txtCustomerName.Text.Trim()); cmd.Parameters.AddWithValue("@Email", txtCustomerEmail.Text.Trim()); cmd.Parameters.AddWithValue("@PhoneNumber", txtCustomerPhone.Text.Trim()); try { await cmd.ExecuteNonQueryAsync(); MessageBox.Show("Customer added successfully!"); // Refresh your customer list here to show the new entry LoadCustomerList(); } catch (Exception ex) { MessageBox.Show($"Failed to add customer: {ex.Message}"); } }
Processing Sales & Inventory Purchases (Data Consistency)
The big risk here is partial updates (e.g., a sale records but stock doesn't decrease). Use database transactions to ensure all operations succeed or fail together:
private async void btnProcessSale_Click(object sender, EventArgs e) { var connectionString = @"Your_Connection_String_Here"; using var conn = new SqlConnection(connectionString); await conn.OpenAsync(); using var transaction = conn.BeginTransaction(); try { // 1. Insert sale record var saleQuery = @"INSERT INTO Sales (CustomerId, BookId, SaleDate, Quantity) VALUES (@CustomerId, @BookId, GETDATE(), @Quantity)"; using var saleCmd = new SqlCommand(saleQuery, conn, transaction); saleCmd.Parameters.AddWithValue("@CustomerId", cboSelectedCustomer.SelectedValue); saleCmd.Parameters.AddWithValue("@BookId", cboSelectedBook.SelectedValue); saleCmd.Parameters.AddWithValue("@Quantity", numSaleQuantity.Value); await saleCmd.ExecuteNonQueryAsync(); // 2. Decrease stock quantity var inventoryQuery = @"UPDATE Inventory SET StockQuantity = StockQuantity - @Quantity WHERE BookId = @BookId"; using var inventoryCmd = new SqlCommand(inventoryQuery, conn, transaction); inventoryCmd.Parameters.AddWithValue("@Quantity", numSaleQuantity.Value); inventoryCmd.Parameters.AddWithValue("@BookId", cboSelectedBook.SelectedValue); await inventoryCmd.ExecuteNonQueryAsync(); // Commit only if both steps work transaction.Commit(); MessageBox.Show("Sale processed successfully!"); LoadInventoryList(); // Refresh inventory to show updated stock } catch (Exception ex) { // Roll back if anything fails transaction.Rollback(); MessageBox.Show($"Sale failed: {ex.Message}"); } }
For inventory purchases, just reverse the inventory update (add instead of subtract) and use a similar transaction flow.
3. Sales Performance Reports
WinForms gives you two solid options for reports:
- Quick Data Grid View: Load filtered sales data into a
DataGridViewfor on-screen viewing:private async void btnGenerateReport_Click(object sender, EventArgs e) { var connectionString = @"Your_Connection_String_Here"; using var conn = new SqlConnection(connectionString); var query = @"SELECT c.FullName AS Customer, b.Title AS Book, s.SaleDate, s.Quantity, b.Price * s.Quantity AS TotalAmount FROM Sales s JOIN Customers c ON s.CustomerId = c.CustomerId JOIN Books b ON s.BookId = b.BookId WHERE s.SaleDate BETWEEN @StartDate AND @EndDate"; using var adapter = new SqlDataAdapter(query, conn); adapter.SelectCommand.Parameters.AddWithValue("@StartDate", dtpReportStart.Value); adapter.SelectCommand.Parameters.AddWithValue("@EndDate", dtpReportEnd.Value); var reportData = new DataTable(); adapter.Fill(reportData); dgvSalesReport.DataSource = reportData; } - RDLC Reports: For printable, formatted reports, use the
Microsoft.Reporting.WinFormslibrary. You can design a report template in Visual Studio and bind it to your sales data—great for professional-looking output.
4. Event Handling Best Practices
- Avoid UI Freezes: Always use
async/awaitfor database operations (like the examples above) so your app stays responsive while waiting for data. - Refresh Data After Actions: After adding a customer, processing a sale, or updating inventory, reload your list controls (like dropdowns or data grids) to show the latest data.
- Validate Input: Add checks for empty fields, valid email formats, or negative quantities before hitting the database—this saves you from unnecessary errors.
If you're stuck on a specific issue (like a weird error message, permission control glitches, or report formatting), share the relevant code snippets or error details, and we can narrow it down further!
内容的提问来源于stack exchange,提问作者RHSkinner

