Reputation: 930
In 2.2 I could write something like this:
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;
In 3.1 it gives me this error:
System.InvalidOperationException: There is already an open DataReader associated with this Command which must be closed first.
But if I add
ToList()
to the forEach clause, it works fine(like this):
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;
Should I change all places in my code where I used similar construct, or there is a way to make EF work normal with it?
Upvotes: 3
Views: 3394
Reputation: 2415
becasue_roleManager.Roles.Where(a => userRoles.Contains(a.Name)
creates an IEnumerator which keeps the connection open, when it try to
do await _roleManager.GetClaimsAsync(role)
again.. it see that it already has a
open DataReader associated
3.0 probably did a internal evaluates the LINQ expression in client side automatically
kind of like an auto Tolist()
, instead of IEnumerator which keeps the connection open.
To avoid this i would do below to all future and current code. That way it doesn't matter, also ensure not executing over IEnumerator statements which re-execute the entire statement.
Further this has the added benefit that you can debug the list before the loop.
Before someone says that this consume more mem, show me a test showing so... This does not use yield and also the compiler is probably smart enough to make optimization for anything it sees as not been needed.
Here they word the IEnumerator issue better but same same.
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;
Upvotes: 1
Reputation: 2679
why ToList()
works?
why it throw expcetion without adding ToList()?
Updates
older version of EF before 3.0 cannot convert expression to SQL or parameter and it evaluates the LINQ expression in client side automatically however new version of EF allow expression in last select
call in query, so if expression in any other part of the query cannot be converted to SQL or parameter then it throws exception
Source Microsoft Docs, can read more here
Upvotes: 1