同一查询多次触发EF

马修

会员
已加入
2020年8月15日
留言内容
23
编程经验
Beginner
我正在为网球运动员及其参加的比赛构建一个非常简单的CRUD Web应用程序(ASP.NET MVC)。
在特定的页面上,我想显示数据库中的所有锦标赛,并在标题的顶部显示“所有锦标赛”页面,并在括号中将数据库中的记录数量括起来。
我的cshtml看起来像这样:

ASP.net:
@model System.Collections.Generic.IEnumerable<TMS.BL.Domain.Tournament>

@{
    ViewBag.Title = "All Tournaments";
    Layout = "_Layout";
}
    <h3>All Tournaments (@Model.Count())</h3>
    
    @if ([email protected]())
{
    <p>No tournaments were found...</p>
}
else
{
    <table class="table">
        <thead>
        <tr>
            <th scope="col">Name</th>
            <th scope="col">Starts</th>
            <th scope="col">Ends</th>
            <th scope="col">Org. Club</th>
            <th scope="col"></th>
        </tr>
        </thead>
        <tbody>
        @foreach (var t in Model)
        {
            <tr>
                <td>@t.Name</td>
                <td>@t.StartDate.ToString("ddd, dd/MM/yyyy")</td>
                <td>@t.EndDate.ToString("ddd, dd/MM/yyyy")</td>
                <td>@t.OrganizingClub.Name (@t.OrganizingClub.Province - @t.OrganizingClub.Town)</td>
                <td>
                    <a asp-controller="Tournament" asp-action="Details" asp-route-id="@t.Id" class="btn btn-primary btn-sm">Details</a>
                </td>
            </tr>
        }
        </tbody>
    </table>
}
}

该页面的控制器是TournamentController。该控制器正在使用包含dbcontext的Manager对象。 GetAllTournamentsWithOrgClubAndParticipants()方法返回一个IEnumerable的锦标赛对象(包括俱乐部和pariticpant,但对我而言,这并不重要)。

C#:
    public class TournamentController : Controller
    {
        private IManager _mgr;

        public TournamentController(IManager manager)
        {
            _mgr = manager;
        }

        public IActionResult Index()
        {
            return View(_mgr.GetAllTournamentsWithOrgClubAndParticipants());
        }

加载页面时,我看到同一查询被触发3次。一次用于网页标题中的@ Model.Count(),一次用于@ Model.Any()确定是否显示该表,一次在foreach循环中。现在我知道这是由于推迟执行而引起的,我可以通过在控制器类的GetAllTournamentsWithOrgClubAndParticipants()之后添加ToList()来解决此问题,但是我经常听到不使用ToList()方法,因为这样您可以加载所有内容进入记忆。但是就我这种情况而言,它比连续执行同一查询3次仍然更好,还是我错了?还有其他方法可以解决这个问题吗?

非常感谢你!
 
Solution
The problem is that LINQ queries are not actualised. They are basically instructions on how to get data, not actual data. Your GetAllTournamentsWithOrgClubAndParticipants method is returning an unactualised LINQ query 和 the actualisation takes place in each of the three places that you use that query.

解决方案是在控制器操作中实现查询,然后将该实际数据传递给您的视图:
C#:
return View(_mgr.GetAllTournamentsWithOrgClubAndParticipants().ToArray());
The ToArray call actualises the LINQ query, which executes the database query 和 loads the results into an array. Your view will then access that array three times. ALWAYS be mindful of when you're using...

金西尼

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
3,501
地点
悉尼,澳大利亚
编程经验
10+
The problem is that LINQ queries are not actualised. They are basically instructions on how to get data, not actual data. Your GetAllTournamentsWithOrgClubAndParticipants method is returning an unactualised LINQ query 和 the actualisation takes place in each of the three places that you use that query.

解决方案是在控制器操作中实现查询,然后将该实际数据传递给您的视图:
C#:
return View(_mgr.GetAllTournamentsWithOrgClubAndParticipants().ToArray());
The ToArray call actualises the LINQ query, which executes the database query 和 loads the results into an array. Your view will then access that array three times. ALWAYS be mindful of when you're using an unactualised query 和 when you're using actual results. Methods like ToList, FirstFirstOrDefault are also often used to actualise a query.
 

马修

会员
已加入
2020年8月15日
留言内容
23
编程经验
Beginner
The problem is that LINQ queries are not actualised. They are basically instructions on how to get data, not actual data. Your GetAllTournamentsWithOrgClubAndParticipants method is returning an unactualised LINQ query 和 the actualisation takes place in each of the three places that you use that query.

解决方案是在控制器操作中实现查询,然后将该实际数据传递给您的视图:
C#:
return View(_mgr.GetAllTournamentsWithOrgClubAndParticipants().ToArray());
The ToArray call actualises the LINQ query, which executes the database query 和 loads the results into an array. Your view will then access that array three times. ALWAYS be mindful of when you're using an unactualised query 和 when you're using actual results. Methods like ToList, FirstFirstOrDefault are also often used to actualise a query.

感谢您的解释!因此,正如您所建议的,我可以使用ToArray()或ToList(),这将一次执行查询,然后在视图中使用此通用数组。但这不是不好的做法吗?
 

金西尼

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
3,501
地点
悉尼,澳大利亚
编程经验
10+
但这不是不好的做法吗?
什么 would be the rationale for that, given that the alternative is to execute the same database query three times? No matter what LINQ provider you're using, it's ALWAYS bad form to enumerate the same LINQ query multiple times. If you need random access to the data (more than once) or to enumerate it multiple times then you should ALWAYS call ToArray or ToList first 和 use the result.
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,500
地点
弗吉尼亚州切萨皮克
编程经验
10+
还可以考虑稍微更改一下UI和逻辑。

Change the check for Any() first. Any() will query 和 stop after the first item is found. If no items are found, you automatically know that Count() will be zero. If it's non-zero, then iterate over each item found, 并保持计数. So you'll query the database again, but this time actually go over all the items found. There's no need to call Count() to get the number of items since you've seen all the items go by 和 have that count readily available to you.
 

马修

会员
已加入
2020年8月15日
留言内容
23
编程经验
Beginner
What would be the rationale for that, given that the alternative is to execute the same database query three times? No matter what LINQ provider you're using, it's ALWAYS bad form to enumerate the same LINQ query multiple times. If you need random access to the data (more than once) or to enumerate it multiple times then you should ALWAYS call ToArray or ToList first 和 use the result.
唯一的事情是,假设这是一个庞大的数据库表,然后将整个表复制到内存中。这不是很糟糕的表现吗?

可能比让它执行相同的查询3次更好,但从理论上讲,在执行查询之间表有可能被更改。因此,计数方法可能会返回5条记录,但是foreach稍后会循环遍历6条记录,以防有人同时添加比赛。
 

金西尼

C#论坛主持人
工作人员
已加入
2011年4月23日
留言内容
3,501
地点
悉尼,澳大利亚
编程经验
10+
这不是很糟糕的表现吗?
当您遍历视图中的数据时,您认为会发生什么?如何将所有数据显示在视图中而不将其加载到内存中?

我想问题是,如果有这么多数据,为什么要一开始就同时显示所有数据?为什么不一次分页并仅显示数据的子集?
 
最佳 底部