【问题标题】:Entity Relations for EF7 MVC6EF7 MVC6 的实体关系
【发布时间】:2025-11-27 22:50:01
【问题描述】:

我的目标是使用 EF7 和 MVC6 [BETA2] 列出书架的数量和每个书架上的书籍数量。

使用正确的表关系正确创建了数据库架构。我可以成功地将书架和书籍添加到数据库中,包括外键关系(见下面的代码)。

当我测试应该在每个书架上显示图书计数的索引页面时,我没有收到图书计数数据,也没有错误。在 Shelf 实体中,Books 属性仍然没有被 Book 实体填充,因此计数为空(参见下面的代码)。

在 EF7 中是否有我需要编写代码来填充 Shelf.Books 的地方,或者这是否应该在 EF7 中自动发生?

BookShelf.cs

namespace MyApp.Models
{
    public class Shelf
    {
        public int ShelfId { get; set; }
        public string Name { get; set; }
        public virtual List<Books> Books { get; set; }
    }

    public class Book
    {
        public int BookId { get; set; }
        public string Name { get; set; }
        public int ShelfId { get; set; }
        public Shelf Shelf{ get; set; }
    }
}

ApplicationDbContext.cs

namespace MyApp
{
    public class ApplicationDBContext
    {
        public DbSet<Shelf> Shelf { get; set; }
        public DbSet<Book> Book { get; set; }
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<Shelf>().Key(s => s.ShelfId);
        builder.Entity<Book>().Key(b => b.BookId);

        builder.Entity<Shelf>()                
            .OneToMany(s => s.Book)
            .ForeignKey(k => k.ShelfId);

        base.OnModelCreating(builder);
    }
}

ShelfController.cs

namespace MyApp
{
    private ApplicationDBContext db;

    public BuildingsController(ApplicationDBContext context)
    {
        db = context;
    }

    // GET: Shelves
    public async Task<IActionResult> Index()
    {
        return View(await db.Shelves.ToListAsync());
    }
}

Index.cshtml

...
@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Books.Count)
        </td>
    </tr>
}
....

【问题讨论】:

  • 可能是因为 db.Shelves.ToListAysnc() 创建了一个在控制器范围之外使用的 Shelves 列表,因此永远不会填充 Books。其次,除非从 6.1 开始发生变化,否则您的 Books 属性应该是 ICollection&lt;Book&gt;。最后,您应该创建一个实际表示视图所需数据的视图模型,而不是依赖视图中的方法来执行逻辑(例如计数,这很可能是这不起作用的原因)。
  • 感谢@ErikPhilips!
  • ICollection&lt;Book&gt; 在 EF7 中已更改为 List&lt;Book&gt;。从您的 cmets 我不确定您是否说它没有填充是因为我没有使用视图模型或因为 db.Shelves.ToListAsync() 超出范围。您建议如何更改?

标签: c# asp.net-core entity-framework-core


【解决方案1】:

看看ICollection Vs List in Entity Framework。我感觉 EF7 使用 List&lt;&gt; 的小例子是不正确的(很难想象使用 EF7 最佳实践从 ICollection&lt;&gt; 更改为 List&lt;&gt;,将具体集合类型公开为财产。)

根据您的评论,我会更改:

创建视图模型

public class IndexViewModel
{
    public IEnumerable<ShelveModel> Shelves { get; set; }
}

public class ShelveModel
{
    public string Name { get; set; }
    public int BookCount { get ; set; }
}

更新逻辑

// GET: Shelves
public async Task<IActionResult> Index()
{
    var model = new IndexViewModel();
    model.Shelves = db.Shelves
        .Select(s => new 
        {
            Name = s.Name,
            BookCount = s.Books.Count()
        })
        .ToListAsync()
        .Select(s => new ShelveModel()
        {
            Name = s.Name,
            BookCount = s.Books.Count()
        })
        .ToList();

    return View(model);
}

【讨论】:

  • Index() 给了我一个时髦的错误An unhandled exception occurred while processing the request. InvalidOperationException: When called from 'VisitMethodCallExpression', expressions of type 'Expression' can only be replaced with other non-null expressions of type 'Expression'. Remotion.Linq.Parsing.ExpressionTreeVisitor.VisitAndConvert[T](T expression, String methodName) 有什么想法吗?
  • 这个问题似乎与shelfIndexViewModel.ShelfModels = await db.Select().....中的await有关,所以它可能是题外话。
  • 代码对async 函数内部缺少await 感到不满。
  • 好的,这是一个非常小的错误,你可以添加等待。
【解决方案2】:

我发现 EF 不会用开箱即用的相关子对象填充父对象。例如,myShelf.Books 将是空的,直到填充到控制器操作函数中。

【讨论】: