ASP.NET C# 复合键插入及账户编辑页数据增改操作技术咨询
Hey there! Let's work through your ASP.NET C# account edit page requirement step by step. I'll cover everything from login redirection, handling insert/update across 3 tables, and solving composite key insertion issues. Here's a practical, implementation-focused approach:
First, let's align on the end-to-end user journey:
- User logs in → system validates credentials → redirect to My Account page
- My Account page fetches and displays all user data from 3 tables
- User edits information → submit form → system runs update/insert logic across all tables → show success message
Assuming you're using EF Core for data access, let's define your entities and DbContext—including handling the composite key table.
2.1 Entity Classes
First, complete the Member entity (based on your truncated table definition) and define a sample composite key entity:
// Member entity (matches your SQL table structure) public class Member { public int MemberID { get; set; } [Required] [MaxLength(30)] public string MemberName { get; set; } [Required] public long Phone { get; set; } [Required] [EmailAddress] public string Email { get; set; } // Add any other fields from your truncated SQL definition } // Example composite key entity (e.g., MemberAddress where MemberID + AddressID = composite key) public class MemberAddress { public int MemberID { get; set; } public int AddressID { get; set; } [Required] public string AddressLine { get; set; } public string City { get; set; } // Navigation property to link back to Member public Member Member { get; set; } } // Third table entity (adjust based on your actual schema) public class MemberPreference { public int PreferenceID { get; set; } public int MemberID { get; set; } public bool ReceiveNewsletter { get; set; } public Member Member { get; set; } }
2.2 DbContext Configuration
Configure keys (including composite keys) and relationships:
public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<Member> Members { get; set; } public DbSet<MemberAddress> MemberAddresses { get; set; } public DbSet<MemberPreference> MemberPreferences { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { // Configure Member primary key (auto-incrementing IDENTITY) modelBuilder.Entity<Member>() .HasKey(m => m.MemberID); // Configure composite key for MemberAddress modelBuilder.Entity<MemberAddress>() .HasKey(ma => new { ma.MemberID, ma.AddressID }); // Configure relationships (optional but recommended) modelBuilder.Entity<MemberAddress>() .HasOne(ma => ma.Member) .WithMany() .HasForeignKey(ma => ma.MemberID); modelBuilder.Entity<MemberPreference>() .HasOne(mp => mp.Member) .WithMany() .HasForeignKey(mp => mp.MemberID); } }
In your AccountController, handle login validation and redirect to the My Account page:
public class AccountController : Controller { private readonly AppDbContext _context; public AccountController(AppDbContext context) { _context = context; } [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Login(LoginViewModel model) { if (!ModelState.IsValid) return View(model); // Validate user (replace with your actual password hashing/validation logic) var member = await _context.Members .FirstOrDefaultAsync(m => m.Email == model.Email && m.Phone == model.Phone); if (member != null) { // Store user ID in session (use ASP.NET Core Identity for production apps!) HttpContext.Session.SetInt32("CurrentMemberID", member.MemberID); return RedirectToAction("MyAccount"); } ModelState.AddModelError("", "Invalid login credentials"); return View(model); } }
4.1 ViewModel for Combined Data
Create a viewmodel to hold all data needed for the edit page:
public class AccountEditViewModel { public Member Member { get; set; } public List<MemberAddress> MemberAddresses { get; set; } public MemberPreference MemberPreference { get; set; } }
4.2 Load Data for the Page
Fetch all user-related data in the MyAccount action:
[HttpGet] public async Task<IActionResult> MyAccount() { var memberId = HttpContext.Session.GetInt32("CurrentMemberID"); if (memberId == null) return RedirectToAction("Login"); // Fetch core member data var member = await _context.Members.FindAsync(memberId); if (member == null) return NotFound(); // Fetch composite key data (MemberAddresses) var addresses = await _context.MemberAddresses .Where(ma => ma.MemberID == memberId) .ToListAsync(); // Fetch third table data (MemberPreference) var preference = await _context.MemberPreferences .FirstOrDefaultAsync(mp => mp.MemberID == memberId) ?? new MemberPreference { MemberID = memberId.Value }; // Initialize if no record exists var viewModel = new AccountEditViewModel { Member = member, MemberAddresses = addresses, MemberPreference = preference }; return View(viewModel); }
4.3 Razor View (Edit Form)
Build a form to let users edit all fields, including composite key records:
@model AccountEditViewModel <h1>My Account</h1> @if (TempData["SuccessMessage"] != null) { <div class="alert alert-success">@TempData["SuccessMessage"]</div> } <form asp-action="MyAccount" method="post"> <h3>Personal Information</h3> <div class="form-group"> <label asp-for="Member.MemberName"></label> <input asp-for="Member.MemberName" class="form-control" /> <span asp-validation-for="Member.MemberName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Member.Phone"></label> <input asp-for="Member.Phone" class="form-control" /> <span asp-validation-for="Member.Phone" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Member.Email"></label> <input asp-for="Member.Email" class="form-control" /> <span asp-validation-for="Member.Email" class="text-danger"></span> </div> <h3>Addresses</h3> @for (int i = 0; i < Model.MemberAddresses.Count; i++) { <div class="card mb-3"> <div class="card-body"> <input type="hidden" asp-for="MemberAddresses[i].MemberID" /> <input type="hidden" asp-for="MemberAddresses[i].AddressID" /> <div class="form-group"> <label asp-for="MemberAddresses[i].AddressLine"></label> <input asp-for="MemberAddresses[i].AddressLine" class="form-control" /> </div> <div class="form-group"> <label asp-for="MemberAddresses[i].City"></label> <input asp-for="MemberAddresses[i].City" class="form-control" /> </div> </div> </div> } <h3>Preferences</h3> <div class="form-check"> <input asp-for="MemberPreference.ReceiveNewsletter" class="form-check-input" /> <label asp-for="MemberPreference.ReceiveNewsletter" class="form-check-label"> Receive monthly newsletter </label> </div> <button type="submit" class="btn btn-primary mt-4">Save Changes</button> </form>
In the MyAccount POST action, process form submissions with transaction support to ensure data consistency:
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> MyAccount(AccountEditViewModel model) { if (!ModelState.IsValid) return View(model); var memberId = HttpContext.Session.GetInt32("CurrentMemberID"); if (memberId == null) return RedirectToAction("Login"); // Use transaction to ensure all operations succeed or fail together using var transaction = await _context.Database.BeginTransactionAsync(); try { // 1. Update Member table var existingMember = await _context.Members.FindAsync(memberId); if (existingMember != null) { existingMember.MemberName = model.Member.MemberName; existingMember.Phone = model.Member.Phone; existingMember.Email = model.Member.Email; _context.Members.Update(existingMember); } // 2. Handle composite key table (MemberAddresses) foreach (var address in model.MemberAddresses) { address.MemberID = memberId.Value; var existingAddress = await _context.MemberAddresses .FirstOrDefaultAsync(ma => ma.MemberID == memberId && ma.AddressID == address.AddressID); if (existingAddress != null) { // Update existing address existingAddress.AddressLine = address.AddressLine; existingAddress.City = address.City; _context.MemberAddresses.Update(existingAddress); } else { // Insert new address (ensure composite key is unique) _context.MemberAddresses.Add(address); } } // 3. Handle third table (MemberPreference) var existingPreference = await _context.MemberPreferences .FirstOrDefaultAsync(mp => mp.MemberID == memberId); if (existingPreference != null) { existingPreference.ReceiveNewsletter = model.MemberPreference.ReceiveNewsletter; _context.MemberPreferences.Update(existingPreference); } else { _context.MemberPreferences.Add(model.MemberPreference); } // Save all changes and commit transaction await _context.SaveChangesAsync(); await transaction.CommitAsync(); TempData["SuccessMessage"] = "Your account information has been updated successfully!"; return RedirectToAction("MyAccount"); } catch (Exception ex) { await transaction.RollbackAsync(); ModelState.AddModelError("", "An error occurred while saving your changes. Please try again."); // Log the exception for debugging return View(model); } }
- Uniqueness Checks: Always validate if the composite key combination exists before inserting to avoid database errors. You can also add a unique constraint in SQL for extra safety.
- Transaction Safety: Use transactions when modifying multiple tables to prevent partial updates if one operation fails.
- Auto-Generate Partial Keys: If one part of the composite key (like
AddressID) is auto-incrementing, ensure your database or EF Core is configured to handle that correctly.
内容的提问来源于stack exchange,提问作者Reina23

