cra*_*aig 4 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,
VehicleNumber = v.VehicleNumber,
InRepair = Convert.ToBoolean(r.InRepair) <-- ITFV field
}
).ToListAsync();
Run Code Online (Sandbox Code Playgroud)
当查询运行时,它会生成一个错误:
InvalidOperationException:在上一个操作完成之前,在此上下文中启动了第二个操作。不保证任何实例成员都是线程安全的。
提到代码:
System.Linq.AsyncEnumerable.Aggregate_(IAsyncEnumerable source, TAccumulate seed, Func accumulator, Func resultSelector, CancellationToken cancelationToken) MyApplication.Controllers.VehiclesController.Foo() in VehiclesController.cs
Run Code Online (Sandbox Code Playgroud)var results = await (
如果我删除 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();
Run Code Online (Sandbox Code Playgroud)
为什么当我添加 ITVF 引用时会发生这种情况?我该如何解决?
UnitRepairStatus
国际电视基金会:
CREATE FUNCTION dbo.UnitRepairStatus(
@unit_number varchar(18)
)
RETURNS TABLE
AS
RETURN
SELECT h.InRepair
-- connects to a second database on same server
-- shouldn't be an issue, but mentioning it in case it might be
FROM Schema2..Unit u
INNER JOIN Schema2..History h on u.ID = h.UnitID
WHERE u.UnitNumber = @unit_number
Run Code Online (Sandbox Code Playgroud)
UnitRepairStatus
模型:
public class UnitRepairStatus
{
public string UnitNumber { get; set; }
public int? InRepair { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
MyDatabaseDbContext
数据库上下文:
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})");
}
Run Code Online (Sandbox Code Playgroud)
FooViewModel
视图模型:
public class FooViewModel
{
public int ID { get; set; }
public string VehicleNumber { get; set; }
public bool InRepair { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
VehiclesController
构造函数:
public VehiclesController(
ILogger<VehiclesController> logger,
MyDatabaseDbContext context
)
{
_logger = logger;
_context = context;
}
Run Code Online (Sandbox Code Playgroud)
VehiclesController
Foo
方法:
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);
}
Run Code Online (Sandbox Code Playgroud)
参考:
对不起,我的错。上一个问题的答案中的技术适用于使用常量/可变参数调用 ITVF,但不适用于像您的情况(以及我的错误示例)中的相关子查询。
解决方案是删除 ITVF 参数并将结果扩展为包括该列(有效地将其转换为无参数视图):
CREATE FUNCTION dbo.UnitRepairStatus()
RETURNS TABLE
AS
RETURN
SELECT u.UnitNumber, h.InRepair
FROM Schema2.Unit u
INNER JOIN Schema2.History h on u.ID = h.UnitID
Run Code Online (Sandbox Code Playgroud)
同时从上下文方法中删除参数
EF 核心 2.x:
public IQueryable<UnitRepairStatus> UnitRepairStatus() =>
Query<UnitRepairStatus>().FromSql("SELECT * FROM UnitRepairStatus()");
Run Code Online (Sandbox Code Playgroud)
EF 核心 3.x:
public IQueryable<UnitRepairStatus> UnitRepairStatus() =>
Set<UnitRepairStatus>().FromSqlRaw("SELECT * FROM UnitRepairStatus()");
Run Code Online (Sandbox Code Playgroud)
并将 LINQ 查询更改为使用 join:
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();
Run Code Online (Sandbox Code Playgroud)
现在它应该翻译并执行服务器端,并在客户端成功实现。
原始方法的问题在于 EF Core 默默地将查询执行切换到客户端评估(讨厌那样),然后保护它在同一个上下文上执行多个异步操作。
归档时间: |
|
查看次数: |
760 次 |
最近记录: |