c# - 如何避免“System.InvalidOperationException:客户端投影包含对常量表达式的引用”错误?
问题描述
我正在编写一个 C# .NET 5 应用程序,它将充当 Angular 前端的后端,提供 CRUD API。该应用程序的目的是管理飞行学校。
我正在编写将返回飞行员列表和单个飞行员的 API 方法,即https://myapp/api/pilots
and https://myapp/api/pilots/{id}
。
我这里有三种方法:
GetPilots()
完整列表GetPilot(long idPilot)
详情- 一种辅助方法
GetFlightTime(long idPilot)
,用于返回每个飞行员的总飞行时间,以及飞行持续时间的总和。
我的问题:detail 方法有效,因为我首先调用辅助函数,然后在返回的视图模型中使用结果。但GetPilots()
不起作用并返回错误System.InvalidOperationException: The client projection contains a reference to a constant expression of 'QTBWeb.Models.PilotsRepository' through the instance method 'GetFlightTime'. This could potentially cause a memory leak; consider making the method static so that it does not capture constant in the instance.
这是因为我GetFlightTime
在 LINQ 表达式中调用该方法吗?我不明白使方法静态建议。如何重新格式化代码以使其正常工作?
谢谢!
public IEnumerable<PilotViewModel> GetPilots() // THIS METHOD RETURNS ERROR
{
return _context.Pilots
.Select(pilot => new PilotViewModel
{
Id = pilot.Id,
Name = pilot.Name,
FlightMinutes = GetFlightTime(pilot.Id) // THE PROBLEM IS HERE
})
.ToList();
}
public PilotViewModel GetPilot(long idPilot) // THIS METHOD WORKS
{
var _flightMinutes = GetFlightTime(idPilot);
return _context.Pilots
.Select(pilot => new PilotViewModel
{
Id = pilot.Id,
Name = pilot.Name,
FlightMinutes = _flightMinutes
})
.Where(pilot => pilot.Id == idPilot)
.FirstOrDefault();
}
public int GetFlightTime(long idPilot)
{
return _context.Flights
.Where(flight => flight.pilot == idPilot)
.Select(flight => flight.Duration).Sum();
}
解决方案
解决这个问题的一个好方法是确保你的Pilot
类有一个集合Flights
,作为你拥有的一对多映射的另一面Flight.Pilot
。然后,您可以使用此集合来计算总和,而无需查询数据库中的每个循环实例Pilot
。
您的代码将如下所示:
public IEnumerable<PilotViewModel> GetPilots()
{
return _context.Pilots
.Include(pilot => pilot.Flights) // Include Flights to join data
.Select(pilot => new PilotViewModel
{
Id = pilot.Id,
Name = pilot.Name,
FlightMinutes = pilot.Flights.Sum(flight => flight.Duration)
});
}
public PilotViewModel GetPilot(long idPilot)
{
return _context.Pilots
.Include(pilot => pilot.Flights) // Include Flights to join data
.Where(pilot => pilot.Id == idPilot) // Notice how we filter first
.Select(pilot => new PilotViewModel
{
Id = pilot.Id,
Name = pilot.Name,
FlightMinutes = pilot.Flights.Sum(flight => flight.Duration)
})
.FirstOrDefault();
}