#c# #http #asp.net-core #asynchronous #get
Вопрос:
Я пытаюсь присвоить свойству класса результат запроса GET, чтобы все другие методы в классе могли использовать значение этого свойства без необходимости вызывать запрос GET более одного раза. Суть в том, что я хочу вызвать запрос GET во время создания экземпляра класса, назначить его переменной, а затем никогда больше не вызывать его во время жизни объектов.
public class Example {
private readonly HttpClient _http;
private readonly List<Thing> _things;
public Example(HttpClient http)
{
_http = http;
_things = _http.GetFromJsonAsync<List<Thing>>("https://api-to-call/endpoint").Resu<
}
public void UseThings()
{
// Do something with _things;
}
}
Однако при вызове метода он отправляет новый запрос GET для получения обновленного значения свойства, которое я назначил в конструкторе. Как я могу закодировать это так, чтобы он вызывал запрос GET только один раз во время создания экземпляра объекта?
Комментарии:
1. Вы не должны выполнять асинхронные вызовы в конструкторе. Вместо этого вы должны сделать это в другом месте потока вашей программы после создания объекта, например, создав
UseThings
методasync
и выполнив его там.2. Как правило, и предполагая , что он не является изменчивым, вы бы кэшировали его где-нибудь (истекает срок действия или нет, зависит от конкретного пользователя/запроса или нет, в зависимости от вашего варианта использования/данных) и только при необходимости обновляли его соответствующим образом.
3. Как вы, вероятно, уже поняли, вы не можете сделать конструктор класса асинхронным. Не заходя в кроличью нору при перепроектировании вашего решения, вы могли бы просто сохранить
Task
его как члена класса, а затем в своих методах ожидать его результата.4. Видишь это: blog.stephencleary.com/2013/01/async-oop-2-constructors.html
5. UseThings() не выполняет GET снова.
Ответ №1:
Вы не должны блокировать в конструкторе и .Результат может иметь неприятные побочные эффекты. Хотя уже обсуждались языковые функции для поддержки этого, до тех пор вам следует перенести это в кэшированную операцию. Вы можете убедиться, что операция выполняется только один раз, заключив http-вызов в a SemaphoreSlim
.
public class Example
{
private readonly HttpClient _http;
private static readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
private List<Thing> _things;
public Example(HttpClient http)
{
_http = http;
}
public async Task UseThings()
{
// Do something with _things;
var localThings = _things ?? await GetThingsAsync();
}
private async Task<List<Thing>> GetThingsAsync()
{
if (_things != null)
{
return await Task.FromResult(_things);
}
await _lock.WaitAsync();
try
{
// double check in case another thread has completed
if (_things != null)
{
return _things;
}
_things = await _http.GetFromJsonAsync<List<Thing>>("https://api-to-call/endpoint");
return _things;
}
finally
{
_lock.Release();
}
}
}
Комментарии:
1. Что вы думаете о том, чтобы сделать оба свойства статическими? т. е. «частный статический HttpClient _http = новый()» и «частная статическая задача<Список<Вещь><Вещь>> = _http. GetFromJsonAsync<Список<Вещь><Вещь>>(«<Вещь>> api для вызова/конечная точка» )». Это помогает предотвратить дополнительные поездки к конечной точке с наименьшим количеством кода.
2. Это заблокирует и, в зависимости от контекста, может привести к тупику. Кроме того, поскольку он статичен, он может даже блокировать запуск приложения, если граф объектов создается во время инициализации.