Я пишу сервис, который отображает некоторые данные. Данные извлекаются из хранилища BLOB-объектов клиентом, у которого есть только метод async
Get
для получения данных. Как только данные извлечены, они используются для заполнения диаграммы, и это делается с помощью другого метода Redraw
, который также async
. Я хотел бы, чтобы график обновлялся каждую минуту или около того, поэтому у меня есть Get
и Redraw
, вызываемые через некоторое время l oop с задержкой.
private async Task Redraw(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var data = await DataProvider.Get();
await Chart.Redraw(data.Labels, new[] {data.DataSet});
await Task.Delay(TimeSpan.FromSeconds(60), cancellationToken);
}
}
К сожалению, это не удается. Я получаю NullReferenceException
и AggregateException
где-то в библиотеке диаграмм. Изменение кода следующим образом работает.
private async Task Redraw(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var data = Task.Run(() => DataProvider.Get()).GetAwaiter().GetResult();
await Task.WhenAll(Chart.Redraw(data.Labels, new[] {data.DataSet}));
await Task.Delay(TimeSpan.FromSeconds(60), cancellationToken);
}
}
Я не уверен, почему первая версия, использующая async / await, терпит неудачу. Я полагаю, что это потому, что Redraw
пытается запустить до завершения задачи данных, но я также полагал, что ожидание метода Get
будет означать, что Redraw
не будет вызываться, пока задача данных не будет завершена. Когда я отлаживаю эти строки, кажется, что данные заполняются до того, как будет выполнена следующая строка, поэтому я немного запутался.
Исключение: «System.NullReferenceException» в Blazorise.Charts.dll Exception брошено: «System.NullReferenceException» в System.Private.CoreLib.dll «iis express .exe» (CoreCLR: clrhost): загружено «C: \ Program Files \ dotnet \ shared \ Microsoft.NETCore.App \ 3.1 0,1 \ System.Diagnostics.StackTrace.dll. 'iis express .exe' (CoreCLR: clrhost): загружен 'C: \ Program Files \ dotnet \ shared \ Microsoft.NETCore.App \ 3.1.1 \ System.Reflection.Metadata.dll'. 'iis express .exe' (CoreCLR: clrhost): загружен 'C: \ Program Files \ dotnet \ shared \ Microsoft.NETCore.App \ 3.1.1 \ System.Collections.Immutable.dll'. Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer: Предупреждение: компонент обработки необработанного исключения: ссылка на объект не установлена на экземпляр объекта.
System.NullReferenceException: ссылка на объект не установлена на экземпляр объекта , at Blazorise.Charts. JS .ToChartDataSet [T] (ChartData 1
data) at
Blazorise.Charts.JS.InitializeChart[TItem,TOptions](IJSRuntime
runtime, DotNetObjectReference
1 dotNetObjectReference, логическое значение hasClickEvent, логическое значение hasHoverEvent, строковое canvasId, тип ChartType, ChartData 1 data, TOptions options, String dataJsonString, String
optionsJsonString) at Blazorise.Charts.BaseChart
4.Initialize ()
в. BaseChart`4.OnAfterRenderAsyn c (Boolean firstRender) Microsoft.AspNetCore.Components.Server.Circuits.CircuitHost: Ошибка: необработанное исключение в цепи 'v_kcV7GT8jgmXOLCuNjM_i918pfAqsa27 *. (Ссылка на объект не установлена на экземпляр объекта.) ---> System.NullReferenceException: Ссылка на объект не установлена на экземпляр объекта. В Blazorise.Charts. JS .ToChartDataSet [T] (ChartData 1
data) at
Blazorise.Charts.JS.InitializeChart[TItem,TOptions](IJSRuntime
runtime, DotNetObjectReference
1 dotNetObjectReference, Boolean hasClickEvent, Boolean hasHoverEvent, String canvasId, тип ChartType, ChartData 1 data, TOptions options, String dataJsonString, String
optionsJsonString) at Blazorise.Charts.BaseChart
4.Initialize ()
в Blazorise.Charts.BaseChart`4.OnAfterRenderAsyn c (логическое значение firstRender End End трассировка стека исключений ---
копаю глубже и я не думайте, что нулевая ссылка вообще выбрасывается при вызове Chart.Redraw
. Когда эта строка выполняется, данные не равны нулю. Вместо этого похоже, что это как-то связано с async / await. Вызов, предшествующий Redraw
, выглядит следующим образом ..
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (!_isAlreadyInitialized && firstRender)
{
_isAlreadyInitialized = true;
await Redraw(_cancellationToken);
}
}
Это переопределяет метод базового класса из библиотеки. Реализация базового класса выглядит следующим образом ..
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
await this.Initialize();
else
await this.Update();
}
Именно этот вызов Initialize
вызывает другой метод ..
private static object ToChartDataSet<T>(ChartData<T> data)
{
return (object) new
{
Labels = data.Labels,
Datasets = ((IEnumerable<object>) data.Datasets).ToList<object>()
};
}
.. и именно здесь находятся данные имеет значение null.
По некоторым причинам использование await / asyn c в методе Redraw
приводит к тому, что базовый класс вызывает Initialize
=> ToChartDataSet
до завершения вызова DataProvider.Get
. При использовании GetAwaiter().GetResult()
, тогда Initialize
не вызывается, пока DataProvider.Get
не завершится.