Проблема с EF Core после миграции с 2.2 на 3.1 - PullRequest
2 голосов
/ 10 января 2020

В 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 ]

1 голос
/ 10 января 2020

почему ToList() работает?

  • ToList () принудительно выполняет запрос select.

почему он вызывает исключение без добавления ToList ()?

  • Будет выброшено исключение, если вы не форсируете выполнение из-за запроса, который фактически выполняется, когда переменная запроса повторяется, а не при создании переменной select

Обновления

более старая версия EF до 3.0 не может преобразовать выражение в SQL или параметр и автоматически оценивает выражение LINQ на стороне клиента, однако новая версия EF разрешает выражение в последнем вызове select в запросе, поэтому, если выражение в любой другой части запроса не может быть преобразуется в SQL или параметр, затем генерируется исключение

Исходные документы Microsoft, можно прочитать подробнее здесь

0 голосов
/ 10 января 2020

, потому что _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, вроде auto Tolist(), вместо IEnumerator, который поддерживает соединение открытым.

Чтобы избежать этого, я бы сделал ниже для всего будущего и текущего кода , Таким образом, это не имеет значения, также убедитесь, что не выполняются операторы IEnumerator, которые повторно выполняют весь оператор.

Кроме того, это дает дополнительное преимущество, заключающееся в том, что вы можете отлаживать список до l oop.

Прежде чем кто-то скажет, что это потребляет больше памяти, покажите мне тест, показывающий так ... Это не использует yield, и компилятор, вероятно, достаточно умен, чтобы оптимизировать все, что он считает ненужным.

Здесь они говорят, что проблема IEnumerator лучше, но то же самое.

Обновление Entity Framework до 6.1.0 с 6.1.x прерывает определенные запросы, если я не включаю 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;
...