Я пытаюсь выполнить запросы EF Core, используя NOLOCK, есть статья, в которой показано, как написать метод расширения, но он не работает должным образом с запросом OData.
Вот ссылка: https://stackoverflow.com/a/63603655
query.ToListAsync выполняет запрос к таблице и пытается получить миллионы записей. Хотя я пытаюсь передать этот запрос с фильтрами
https://localhost:60484/odata/Users?$filter=userId eq 123&$expand=Products,Orders
По сути, я пытаюсь получить записи из пользовательской таблицы, где user id = 123, а также хочу расширить свойства внешнего ключа продуктов и заказов.
// Controller method
[HttpGet]
[EnableQuery]
public async Task<IQueryable<User>> Get()
{
return await _repository.UserRepository.GetUserData();
}
// Repository base
public async Task<List<T>> FindAll() => await _dbContext.Set<T>().AsNoTracking().ToListWithNoLockAsync();
// Repository method
public async Task<IQueryable<User>> GetUserData()
{
var data = await FindAll();
return data.AsQueryable()
.Include(a => a.Products)
.Include(a => a.Orders);
}
// Extension method from the article
public static class EFCoreTransaction
{
public static async Task<List<T>> ToListWithNoLockAsync<T>(
this IQueryable<T> query,
Expression<Func<T, bool>>? expression = null,
CancellationToken cancellationToken = default)
{
List<T>? result = default;
using (var scope = CreateTransaction())
{
if (expression != null)
{
query = query.Where(expression);
}
result = await query.ToListAsync(cancellationToken);
scope.Complete();
}
return result!;
}
private static TransactionScope CreateTransaction()
{
return new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions()
{
IsolationLevel = IsolationLevel.ReadUncommitted
},
TransactionScopeAsyncFlowOption.Enabled);
}
}
Что ж, я протестировал SQL-запрос, который генерируется в журналах, а также проследил журналы с помощью нашего собственного инструмента трассировки, нигде он не применяет nolock и не читает незафиксированные данные. Я знаю, что применять/добавлять nolock к запросу из EF — очень плохая идея, но я не уверен, что не так с чтением без фиксации? Основная проблема заключается в том, что вызов get зависает, если есть другие процессы, пытающиеся обновить тот же объект в области транзакции, тогда он блокируется, и ему приходится очень долго ждать, чтобы получить результаты GET. Я не нашел лучшего способа применить чтение без фиксации.
У нас есть очень тяжелые процессы/выигрышные службы SSIS, которые постоянно пытаются обновить один и тот же объект, из которого я пытаюсь GET. Вы также можете проверить это самостоятельно, просто создайте конечную точку GET и запустите запрос выбора в БД в области транзакции и не завершайте его, затем вернитесь и нажмите GET EP, вы не получите результатов и будете вынуждены дождаться этого момента, пока вы не откатите или не завершите транзакцию из базы данных. Затем примените вышеуказанное решение, и вы получите плавные результаты. Дайте мне знать, если я ошибаюсь или есть какие-либо другие идеи, я открыт для обсуждения.
Просто чтобы убедиться, что мы находимся на одной волне: nolock — это более или менее то же самое, что чтение без фиксации. Проблема, конечно, в том, что вы читаете данные, которые, возможно, никогда не сохранятся. Если вы полагаетесь на данные для дальнейшей обработки, все может пойти не так. Настоящая проблема — это длительная транзакция, возможно, с сериализуемой изоляцией (по умолчанию для области транзакции), которая блокирует все, что она читает (на SQL Server). Если что-то и должно измениться, так это сам процесс. Рассмотрим оптимистический параллелизм.





Я нашел способ сделать это. В конце концов я могу оставить классы репозитория нетронутыми и просто создать класс расширения, как указано в статье. Затем в контроллере я внес эти изменения с помощью ODataQueryOptions. Поскольку Iqueryable фактически не выполняет запрос к БД, он извлекает записи, а затем применяет ToList() с незафиксированным чтением. Любое экспертное мнение приветствуется. Я проверил это, проверив журналы и профилировщик SQL. Сгенерированный запрос выполняется в области транзакции.
[HttpGet]
[EnableQuery]
public async Task<IActionResult> Get(ODataQueryOptions<User> options)
{
IQueryable<User>? tempQuery = _repository.UserRepository.GetUserData();
if (options.Filter != null)
{
tempQuery = options.Filter.ApplyTo(tempQuery, new ODataQuerySettings()) as IQueryable<User>;
}
if (options.SelectExpand != null)
{
Request.ODataFeature().SelectExpandClause = options.SelectExpand.SelectExpandClause;
}
IQueryable<User> result = (await tempQuery!.ToListWithNoLockAsync()).AsQueryable();
return Ok(result);
}
Я уверен, вы знаете, что чтение незафиксированных данных — очень плохая идея? Я действительно не понимаю, почему конечная точка OData должна иметь эту «функцию». Какую основную проблему вы пытаетесь решить? Должны быть лучшие способы.