Как вызвать некоторый асинхронный код в ASP.NET application_start
В нашем приложении application_startup мы запустили нашу базу данных с некоторыми поддельными данными, если данных не существует.
Для этого мы используем методы Async
для хранения данных. Отлично. Единственная проблема заключается в том, что мы не знаем, как это сделать в application_startup
, потому что это не метод асинхронизации.
Я потратил столько времени, пытаясь понять учебники @StevenCleary, и я всегда получаю тупики. Я полностью понимаю, что он постоянно говорит:
Как правило, вы должны использовать "async all the down"; то есть не блокировать асинхронный код
но я просто не понимаю, как это сделать, в этом случае: (
Позволяет себе представить, что это код, который я пытаюсь воспроизвести с помощью...
protected void Application_Start()
{
var someFakeData = LoadSomeFakeData();
var documentStore = new DocumentStore();
await documentStore.InitializeAsync(someFakeData);
...
// Registers this database as a singleton.
Container.Register(documentStore);
}
а затем.. некоторый код, который использует этот documentStore
. Он впрыскивается посредством инъекции конструкции...
public SomeController(IDocumentStore documentStore)
{
_documentStore = documentStore;
}
public ViewModel GetFoos()
{
using (var session = _documentStore.OpenSession())
{
... db code goes in here ...
}
}
Разъяснение
Я не пытаюсь сделать какой-то асинхронный код здесь. Я на самом деле пытаюсь вызвать этот метод async, синхронно. Конечно, я теряю преимущества async blah blah de blah.. но я доволен этим. Это запускается, и я счастлив заблокировать при запуске.
Ответы
Ответ 1
В этом случае вы асинхронно инициализируете общий ресурс. Поэтому я рекомендую вам либо сохранить сам Task
, либо ввести асинхронный тип оболочки.
Использование Task
:
protected void Application_Start()
{
var someFakeData = LoadSomeFakeData();
var documentStore = new DocumentStore();
var documentStoreTask = documentStore.InitializeAsync(someFakeData);
...
// Registers this database task as a singleton.
Container.Register(documentStoreTask);
}
Это может быть слишком неудобно, однако, в зависимости от Container
. В этом случае вы можете ввести асинхронный тип оболочки:
public sealed class DocumentStoreWrapper
{
private readonly Task<DocumentStore> _documentStore;
public DocumentStoreWrapper(Data data)
{
_documentStore = CreateDocumentStoreAsync(data);
}
private static async Task<DocumentStore> CreateDocumentStoreAsync(Data data)
{
var result = new DocumentStore();
await documentStore.InitializeAsync(data);
...
return result;
}
public Task<DocumentStore> DocumentStoreTask { get { return _documentStore; } }
}
protected void Application_Start()
{
var someFakeData = LoadSomeFakeData();
var documentStoreWrapper = new DocumentStoreWrapper(someFakeData);
...
// Registers this database wrapper as a singleton.
Container.Register(documentStoreWrapper);
}
Или вы можете использовать AsyncLazy<T>
, который делает то же самое, но использует фоновый поток для выполнения кода инициализации.
Ответ 2
Ваш ожидаемый метод InitializeAsync() должен возвращать Задачу. Вы можете сделать этот вызов синхронно, ожидая завершения задачи.
Один из способов сделать это в коде:
documentStore.InitializeAsync().Wait();
Вы можете быть в порядке, потому что это происходит при запуске приложения. Это должно произойти только один раз для запуска приложения.
Однако вы можете также рассмотреть возможность запуска его в другом потоке. Я не знаю, что вы в значительной степени выиграете, сделав это, в вашем случае. Вероятно, вы не хотите начинать обработку других веб-запросов до тех пор, пока эта работа по запуску не будет выполнена. В конце концов, это настройка вашего хранилища документов, который, как я полагаю, станет ключевой частью ваших будущих запросов приложений.
Вы можете узнать больше о ожидании выполнения задач:
https://msdn.microsoft.com/en-us/library/dd537610(v=vs.110).aspx
Вы можете утверждать, что ваш метод инициализации не обязательно должен поддерживать async. Но это дизайнерское решение для вас. Лучшая причина для поддержки асинхронного вызова вашей базы данных заключается в том, что если все сделано правильно, потоки, обслуживающие эти веб-запросы, могут бесплатно обслуживать другие веб-запросы во время завершения операции с базой данных. Все это происходит в миллисекундах. Однако, если ваш веб-сайт может обслуживать статический файл в 12 мс, но ему требуется 50 мс для записи в базу данных (составленные номера), тогда этот поток, возможно, подобрал и выполнил еще несколько запросов за это время.
Ответ 3
По соглашению xxxAsync методы должны возвращать Task
и отсюда вы можете позвонить
task.RunSynchronously()
https://msdn.microsoft.com/en-us/library/dd321435%28v=vs.110%29.aspx
Ответ 4
public static class AsyncHelper
{
private static readonly TaskFactory MyTaskFactory = new
TaskFactory(CancellationToken.None,
TaskCreationOptions.None,
TaskContinuationOptions.None,
TaskScheduler.Default);
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return MyTaskFactory
.StartNew(func)
.Unwrap()
.GetAwaiter()
.GetResult();
}
public static void RunSync(Func<Task> func)
{
MyTaskFactory
.StartNew(func)
.Unwrap()
.GetAwaiter()
.GetResult();
}
}
то используйте
AsyncHelper.RunSync(ProcessAsync);
private async Task ProcessAsync(){ ....