You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在.NET Core书店项目中实现添加新书时读取展示作者的问题

嘿,我看你正在搭建.NET Core的虚构书店项目,要实现添加新书时让用户选择现有作者的功能对吧?这是个典型的多对多关联场景,我给你梳理一套完整的实现方案:

步骤1:确保EF Core多对多关联配置正确

首先得确保你的DbContext和实体类正确配置了Books、Authors以及中间表BookAuthors的多对多关系,这样EF才能正确处理关联数据:

// DbContext配置
public class BookStoreDbContext : DbContext
{
    public BookStoreDbContext(DbContextOptions<BookStoreDbContext> options) : base(options) { }

    public DbSet<Book> Books { get; set; }
    public DbSet<Author> Authors { get; set; }
    public DbSet<BookAuthor> BookAuthors { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 配置中间表的复合主键
        modelBuilder.Entity<BookAuthor>()
            .HasKey(ba => new { ba.BookId, ba.AuthorId });

        // 建立Book与BookAuthor的关联
        modelBuilder.Entity<BookAuthor>()
            .HasOne(ba => ba.Book)
            .WithMany(b => b.BookAuthors)
            .HasForeignKey(ba => ba.BookId);

        // 建立Author与BookAuthor的关联
        modelBuilder.Entity<BookAuthor>()
            .HasOne(ba => ba.Author)
            .WithMany(a => a.BookAuthors)
            .HasForeignKey(ba => ba.AuthorId);
    }
}

// Book实体类
public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public decimal Price { get; set; }
    // 其他书籍属性(比如出版日期、ISBN等)
    public ICollection<BookAuthor> BookAuthors { get; set; } = new List<BookAuthor>();
}

// Author实体类
public class Author
{
    public int Id { get; set; }
    public string Name { get; set; }
    // 其他作者属性(比如出生日期、简介等)
    public ICollection<BookAuthor> BookAuthors { get; set; } = new List<BookAuthor>();
}

// 中间表实体类
public class BookAuthor
{
    public int BookId { get; set; }
    public Book Book { get; set; }
    public int AuthorId { get; set; }
    public Author Author { get; set; }
}
步骤2:创建用于视图的ViewModel

因为添加页面需要同时处理新书信息和作者选择列表,直接用实体类不太方便,所以创建一个ViewModel来封装这些数据:

public class BookCreateViewModel
{
    // 新书的基本属性,和Book实体对应
    public string Title { get; set; }
    public decimal Price { get; set; }
    // 按需添加其他书籍字段...

    // 存储用户选中的作者ID数组(多对多支持多选)
    public int[] SelectedAuthorIds { get; set; }
    // 供视图渲染的作者选项列表
    public SelectList AuthorOptions { get; set; }
}
步骤3:完善BookController的Add方法

接下来修改你的BookController,在GET方法中加载所有作者并填充ViewModel,POST方法中处理表单提交并保存关联数据:

public class BookController : Controller
{
    private readonly BookStoreDbContext _context;

    // 通过依赖注入获取DbContext实例
    public BookController(BookStoreDbContext context)
    {
        _context = context;
    }

    // GET: Book/Add
    public async Task<IActionResult> Add()
    {
        // 从数据库获取所有作者,按姓名排序提升用户体验
        var authors = await _context.Authors.OrderBy(a => a.Name).ToListAsync();
        // 初始化ViewModel并填充作者选项
        var viewModel = new BookCreateViewModel
        {
            AuthorOptions = new SelectList(authors, "Id", "Name")
        };
        return View(viewModel);
    }

    // POST: Book/Add
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Add(BookCreateViewModel viewModel)
    {
        if (ModelState.IsValid)
        {
            // 从ViewModel创建Book实体
            var newBook = new Book
            {
                Title = viewModel.Title,
                Price = viewModel.Price,
                // 映射其他书籍属性...
            };

            // 先保存新书,获取自动生成的BookId
            _context.Books.Add(newBook);
            await _context.SaveChangesAsync();

            // 如果用户选中了作者,批量添加到中间表
            if (viewModel.SelectedAuthorIds != null && viewModel.SelectedAuthorIds.Any())
            {
                var bookAuthorRelations = viewModel.SelectedAuthorIds
                    .Select(authorId => new BookAuthor
                    {
                        BookId = newBook.Id,
                        AuthorId = authorId
                    });

                _context.BookAuthors.AddRange(bookAuthorRelations);
                await _context.SaveChangesAsync();
            }

            // 保存成功后跳转到书籍列表页
            return RedirectToAction(nameof(Index));
        }

        // 如果模型验证失败,重新加载作者选项并返回视图
        var authors = await _context.Authors.OrderBy(a => a.Name).ToListAsync();
        viewModel.AuthorOptions = new SelectList(authors, "Id", "Name");
        return View(viewModel);
    }
}
步骤4:编写Add视图

最后编写Add.cshtml视图,用复选框让用户选择多个作者(多对多场景下复选框比下拉框更友好):

@model BookCreateViewModel

@{
    ViewData["Title"] = "Add New Book";
}

<h1 class="mb-4">Add New Book</h1>

<form asp-action="Add">
    <div asp-validation-summary="ModelOnly" class="text-danger mb-3"></div>

    <!-- 书籍标题输入框 -->
    <div class="form-group mb-3">
        <label asp-for="Title" class="form-label"></label>
        <input asp-for="Title" class="form-control" />
        <span asp-validation-for="Title" class="text-danger"></span>
    </div>

    <!-- 书籍价格输入框 -->
    <div class="form-group mb-3">
        <label asp-for="Price" class="form-label"></label>
        <input asp-for="Price" class="form-control" />
        <span asp-validation-for="Price" class="text-danger"></span>
    </div>

    <!-- 作者选择区域 -->
    <div class="form-group mb-3">
        <label class="form-label">Select Authors</label>
        <div class="row">
            @foreach (var authorOption in Model.AuthorOptions)
            {
                <div class="col-md-4 mb-2">
                    <div class="form-check">
                        <input class="form-check-input" 
                               type="checkbox" 
                               name="SelectedAuthorIds" 
                               value="@authorOption.Value"
                               @(Model.SelectedAuthorIds != null && Model.SelectedAuthorIds.Contains(int.Parse(authorOption.Value)) ? "checked" : "") />
                        <label class="form-check-label">@authorOption.Text</label>
                    </div>
                </div>
            }
        </div>
    </div>

    <div class="form-group">
        <button type="submit" class="btn btn-primary">Save Book</button>
        <a asp-action="Index" class="btn btn-secondary ms-2">Back to List</a>
    </div>
</form>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
额外注意点
  • 如果使用的是EF Core 5及以上版本,你可以省略显式的BookAuthor中间实体,直接在Book类中定义public ICollection<Author> Authors { get; set; },在Author类中定义public ICollection<Book> Books { get; set; },EF会自动创建中间表。但显式定义中间实体的好处是,后续如果需要给中间表添加额外字段(比如作者的合作顺序),扩展起来更方便。
  • 确保在Program.cs(.NET 6+)或Startup.cs中正确注册DbContext,依赖注入才能正常工作:
    // .NET 6+ Program.cs示例
    builder.Services.AddDbContext<BookStoreDbContext>(options =>
        options.UseSqlServer(builder.Configuration.GetConnectionString("BookStoreConnection")));
    
  • 视图中的复选框name属性必须和ViewModel的SelectedAuthorIds字段名一致,这样ASP.NET Core的模型绑定才能正确接收用户选中的作者ID数组。

内容的提问来源于stack exchange,提问作者Kaloyan Drenski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:37:46