【问题标题】:Bad performance in rendering the Web page with EF6+MVC5. Why?使用 EF6+MVC5 呈现网页时性能不佳。为什么?
【发布时间】:2026-01-09 06:20:06
【问题描述】:

我使用 .NET 框架 4.5.1、MVC 5 和 EF 6 开发了一个 Web 应用程序。数据存储在 MySQL 数据库中。 问题是表演真的很糟糕,我找不到原因。有些页面需要几秒钟(最多 10 秒钟)才能完全加载!

我已尝试实施在网上找到的一些建议,但效果不大。

作为实例,以下控制器从数据库中获取用户列表并将其传递给视图。

public class UserController : Controller
{
    // GET: /User/
    public async Task<ActionResult> Index()
    {
        using (AuthorDbContext db = new AuthorDbContext())
        {
            return View(await db.Users.OrderBy(u => u.Surname)
                                        .ThenBy(u => u.Name)
                                        .Include(u => u.Company)
                                        .Include(u => u.Department)
                                    .ToListAsync());
        }
    }

    // [...]
}

视图将用户的详细信息打印到 HTML 表中(使用 jQuery 的DataTable plugin)。

@model IEnumerable<Author.Models.User>
@using Author.Helpers

<div class="geco-button-container">
    @ButtonHelper.CreateButton(Url.RouteUrl("New User"), "Add a new user")
</div>

<table id="longTable" class="longTable">
    <thead>
        <tr>
            <th></th>
            <th>
                @Html.DisplayNameFor(model => model.Name)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Surname)
            </th>
[...]
        </tr>
    </thead>
    <tbody>
    @foreach (var item in Model) {
        <tr@(item.QuittedOn != null && item.QuittedOn.Value < DateTime.Now ? " class=user_dismissed" : "")>
            <td class="tableButton">
                @ButtonHelper.ViewButton(Url.RouteUrl("View User", new RouteValueDictionary {{ "id", item.id }}), "", "Details")
                @if (item.canBeEdited)
                {
                    @ButtonHelper.EditButton(Url.RouteUrl("Edit User", new RouteValueDictionary {{ "id", item.id }}), "", "Edit")
                    @ButtonHelper.DeleteButton(Url.RouteUrl("Delete User", new RouteValueDictionary {{ "id", item.id }}), "", "Delete")
                }
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Name)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Surname)
            </td>
[...]
            <td>
                @if (item.Department != null)
                {
                    @Html.RouteLink(item.Department.Code.ToString(), "View Department", new RouteValueDictionary { { "id", item.DepartmentId } }, new Dictionary<string,object> { { "title", item.Department.Description.ToString() } })
                }
            </td>
        </tr>
    }
    </tbody>
</table>
@section Scripts {
    @Scripts.Render("~/Scripts/DataTables-1.9.4/media/js/jquery.dataTables.min.js")
    @Scripts.Render("~/Scripts/longTable.js")
}

当我访问此页面时,需要 5 到 10 秒才能完成加载 698 个用户的列表。

我已经安装了Glimpse,它说问题似乎出在结果页面的渲染上。

如您所见,我实施了一些可以提高性能的技巧,例如:

  • 使用RouteLinkRouteUrl 代替ActionLinkActionUrl
  • 使用async 调用
  • 禁用除 RazorViewEngine 之外的所有视图引擎(在 Global.asax 中)

我正在本地机器上以调试模式运行应用程序。有人建议将选项 debug="false" 设置到 web.config 中,但我也将其发布在使用该选项运行的 Web 服务器上,但问题仍然存在。

很多人抱怨实体框架的慢,但他们通常谈论数百毫秒,而在我的情况下需要几秒钟 - 这在 Web 场景中确实是不可接受的!

有什么问题?我做错了什么?


更新

我已经安装了 dotTRACE,这是显示用户列表的页面上的结果。

如您所见,瓶颈在于 Microsoft 框架。

什么是 MappingDataUtilities 类?它的目标是什么?它的函数引用了 Json、Css、TagName、Selectors 等东西呢?

我的代码从不明确调用这些东西。我怎样才能改善他们的不良表现?

【问题讨论】:

  • 摆脱异步的东西,看看这是否与性能有关 + 获得一个体面的分析器,它可以告诉你慢的地方。我建议 dotTracer + 找出这个问题是否与网络有关?如果您从控制台应用程序调用 Index() 会怎样..
  • 根据描述的症状,每个项目都需要额外的数据库查询,比如延迟加载额外的数据。虽然您有一些 Includes 在查询中,但可能有额外的逻辑在渲染期间查询每个项目的 DB。
  • 如果您注释掉初始化 DataTable 的脚本(可能是 longtable.js),您是否最终得到了您正在寻找的性能,尽管使用的是长 HTML 表格而不是格式化的网格?大部分执行时间似乎在客户端。服务器上的亚秒级(查询+视图渲染)时间不会向我提出任何危险信号。根据我的经验,您需要在 DataTables 插件中使用“服务器端处理”选项,以便随着行数的增加获得良好的性能。
  • 代码只生成一个查询,因为我使用Include,然后ToList()指令触发查询执行,因此View不需要任何额外的查询。 @Lanorkin,为了确定,我尝试将this.Configuration.LazyLoadingEnabled = false; 设置到 DBContext 中,但没有任何变化。
  • @Lanorkin,好建议。但是,加载之前保存在我的桌面上的静态 HTML 页面需要 2 秒 - 不到 6 秒。实际上,我使用了很多东西:FontAwesome、Bootstrap、jQuery、DataTables、jQuery UI……但我认为真正的问题出在其他地方。

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


【解决方案1】:

我强烈建议您将 ViewModel 与 DataModel 分离。当这两个是同一件事时,这意味着将所有内容都暴露给您的 ViewModel,即使您只需要少数几个字段。我建议创建一个仅包含您感兴趣的字段的新 ViewModel,然后更新您的 EF 查询以仅选择您需要的字段,而不是在许多表上执行 SELECT *。我相信这对性能有很大帮助。

【讨论】:

    最近更新 更多