C# 引用ITVF会引发“在上一个操作完成之前在此上下文上启动的第二个操作”异常
我试图在Linq查询中引用内联表值函数ITVF:C# 引用ITVF会引发“在上一个操作完成之前在此上下文上启动的第二个操作”异常,c#,linq,asp.net-core,asp.net-core-2.1,entity-framework-core-2.1,C#,Linq,Asp.net Core,Asp.net Core 2.1,Entity Framework Core 2.1,我试图在Linq查询中引用内联表值函数ITVF: var results = await ( from v in _context.Vehicles from r in _context.UnitRepairStatus(v.VehicleNumber) <-- ITVF reference orderby v.VehicleNumber select new FooViewModel { ID = v.ID,
var results = await (
from v in _context.Vehicles
from r in _context.UnitRepairStatus(v.VehicleNumber) <-- ITVF reference
orderby v.VehicleNumber
select new FooViewModel {
ID = v.ID,
VehicleNumber = v.VehicleNumber,
InRepair = Convert.ToBoolean(r.InRepair) <-- ITFV field
}
).ToListAsync();
如果删除ITFV引用,查询将按预期工作
var results = await (
from v in _context.Vehicles
orderby v.VehicleNumber
select new FooViewModel {
ID = v.ID,
VehicleNumber = v.VehicleNumber,
InRepair = False <-- dummy value
}
).ToListAsync();
单元状态模型:
MyDatabasedContext数据库上下文:
public class MyDatabaseDbContext : DbContext
{
public MyDatabaseDbContext(DbContextOptions<MyDatabaseDbContext> options) : base(options) {}
...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
...
modelBuilder.Query<UnitRepairStatus>();
}
public IQueryable<UnitRepairStatus> UnitRepairStatus(string unitNumber) =>
Query<UnitRepairStatus>().FromSql($"SELECT * FROM UnitRepairStatus({unitNumber})");
}
车辆控制器构造:
public VehiclesController(
ILogger<VehiclesController> logger,
MyDatabaseDbContext context
)
{
_logger = logger;
_context = context;
}
参考:
对不起,是我的错。上一个问题的答案中的技术适用于使用常量/变量参数调用ITVF,但不适用于相关子查询,如您的案例和我的错误示例 解决方案是删除ITVF参数,并扩展结果以包括该列,同时有效地将其转换为无参数视图: 创建函数dbo.UnitRepairStatus 返回表 像 回来 在准备中选择u.UnitNumber,h 来自Schema2.u单元 内部联接模式2.u.ID上的历史h=h.UnitID 还要从上下文方法中删除该参数 EF Core 2.x:
public IQueryable<UnitRepairStatus> UnitRepairStatus() =>
Query<UnitRepairStatus>().FromSql("SELECT * FROM UnitRepairStatus()");
EF Core 3.x:
public IQueryable<UnitRepairStatus> UnitRepairStatus() =>
Set<UnitRepairStatus>().FromSqlRaw("SELECT * FROM UnitRepairStatus()");
并将LINQ查询更改为使用联接:
var results = await (
from v in _context.Vehicles
join r in _context.UnitRepairStatus() on v.VehicleNumber equals r.UnitNumber // <---
orderby v.VehicleNumber
select new FooViewModel {
ID = v.ID,
VehicleNumber = v.VehicleNumber,
InRepair = Convert.ToBoolean(r.InRepair)
}
).ToListAsync();
现在,它应该在服务器端翻译和执行,并在客户端成功实现
原始方法的问题是EF Core悄悄地将查询执行切换到客户端,然后在同一个上下文上执行多个异步操作,从而达到其保护作用。使用无参数的TVF而不仅仅是视图有什么价值?如果没有,我可以消除IQueryable方法,将其定义为modelBuilder.queryUnitRepairStatus。您的代码的其余部分似乎可以按原样工作。原始方法的问题是EF Core会将查询执行悄悄地切换到客户端,然后在同一上下文上执行多个异步操作,从而达到保护效果。当我使用第二个上下文连接到另一个数据库时,我没有得到错误,可能是因为它没有达到操作限制。这也解释了为什么输出显示对函数的DB的重复调用。查询按预期进行了转换:从[Vehicles]中选择[v].[ID],[v].[VehicleNumber],[r].[InRepair]作为[v]上的[v]内部联接从UnitRepairStatus选择*作为[r]。[VehicleNumber]=[r].[UnitNumber]按[VehicleNumber]订购@craig的确,没有参数可以映射到视图。仍然使用函数更灵活-它允许您定义和传递一些其他非相关参数,并且在将来的某个时候,我希望对TVF集返回函数的支持将被添加,类似于v2.0中添加的对[Database scalar function mapping]的支持。上述技术只是当前EF核心限制的变通方法。
public VehiclesController(
ILogger<VehiclesController> logger,
MyDatabaseDbContext context
)
{
_logger = logger;
_context = context;
}
public async Task<IActionResult> Foo()
{
List<FooViewModel> model = null;
try
{
var results = await ( <-- line referenced in error message
from v in _context.Vehicles
from r in _context.UnitRepairStatus(v.VehicleNumber)
orderby v.VehicleNumber
select new FooViewModel {
ID = v.ID,
VehicleNumber = v.VehicleNumber,
InRepair = Convert.ToBoolean(r.InRepair)
}
).ToListAsync();
}
catch (Exception e)
{
_logger.LogError(e.Message);
throw;
}
return View(model);
}
public IQueryable<UnitRepairStatus> UnitRepairStatus() =>
Query<UnitRepairStatus>().FromSql("SELECT * FROM UnitRepairStatus()");
public IQueryable<UnitRepairStatus> UnitRepairStatus() =>
Set<UnitRepairStatus>().FromSqlRaw("SELECT * FROM UnitRepairStatus()");
var results = await (
from v in _context.Vehicles
join r in _context.UnitRepairStatus() on v.VehicleNumber equals r.UnitNumber // <---
orderby v.VehicleNumber
select new FooViewModel {
ID = v.ID,
VehicleNumber = v.VehicleNumber,
InRepair = Convert.ToBoolean(r.InRepair)
}
).ToListAsync();