从2.2迁移到3.1后,EF Core出现问题

时间:2020-01-10 08:06:46

标签: c# asp.net-core entity-framework-core ef-core-3.1

在2.2版中,我可以这样写:

        List<Claim> claims = new List<Claim>();
        var userRoles = await _userManager.GetRolesAsync(user);
        foreach (var role in _roleManager.Roles.Where(a => userRoles.Contains(a.Name)))
        {
            claims.AddRange(await _roleManager.GetClaimsAsync(role));
        }

        return claims;

在3.1中,它给了我这个错误:

System.InvalidOperationException:已经有与此命令关联的打开的DataReader,必须先关闭它。

但是,如果我添加

ToList()

对于forEach子句,它可以正常工作(像这样):

        List<Claim> claims = new List<Claim>();
        var userRoles = await _userManager.GetRolesAsync(user);
        foreach (var role in _roleManager.Roles.Where(a => userRoles.Contains(a.Name)).ToList())
        {
            claims.AddRange(await _roleManager.GetClaimsAsync(role));
        }

        return claims;

我应该在代码中更改使用相似构造的所有位置,还是有办法使EF正常工作?

2 个答案:

答案 0 :(得分:1)

为什么ToList()有效?

  • ToList()强制在选择查询上执行。

为什么不添加ToList()就抛出期望值?

  • 如果在迭代查询变量时由于实际执行查询而没有强制执行,则会抛出异常 创建选择变量时不提供

更新

3.0之前的EF的较旧版本无法将表达式转换为SQL或参数,并且它会自动在客户端评估LINQ表达式,但是EF的新版本允许在查询的最后select次调用中使用表达式,因此如果在其他表达式中查询的一部分无法转换为SQL或参数,然后引发异常

来源Microsoft文档,可以阅读更多here

答案 1 :(得分:0)

因为_roleManager.Roles.Where(a => userRoles.Contains(a.Name)创建了一个IEnumerator,当它尝试进行连接时

再次执行await _roleManager.GetClaimsAsync(role)。发现它已经有一个 open DataReader associated

3.0可能执行了内部evaluates the LINQ expression in client side automatically的工作,就像自动Tolist() 一样,而不是使用IEnumerator来保持打开状态。

为避免这种情况,我将在下面对所有将来和当前的代码进行处理。 这样就没关系了,还请确保不要在IEnumerator语句上执行,该语句会重新执行整个语句。

这还有一个额外的好处,您可以在循环之前调试列表。

在有人说这会消耗更多的内存之前,请先给我看一个测试,显示... 这不使用yield,而且编译器可能足够聪明,可以对不需要的内容进行优化。

他们说IEnumerator问题更好,但相同。

Entity Framework upgrade to 6.2.0 from 6.1.x breaks certain queries unless I enable MARS

List<Claim> claims = new List<Claim>();
var userRoles = await _userManager.GetRolesAsync(user);
//you could change this to use async and await
//var roles = await _roleManager.Roles.Where(a => userRoles.Contains(a.Name)).ToListAsync();
var roles = _roleManager.Roles.Where(a => userRoles.Contains(a.Name)).ToList();
foreach (var role in roles)
{
    claims.AddRange(await _roleManager.GetClaimsAsync(role));
}

return claims;