Llamando al método asincrónico sincrónicamente

Tengo un método async :

 public async Task GenerateCodeAsync() { string code = await GenerateCodeService.GenerateCodeAsync(); return code; } 

Necesito llamar a este método desde un método sincrónico.

¿Cómo puedo hacer esto sin tener que duplicar el método GenerateCodeAsync para que esto funcione sincrónicamente?

Actualizar

Sin embargo, no se encontró una solución razonable.

Sin embargo, veo que HttpClient ya implementa este patrón

 using (HttpClient client = new HttpClient()) { // async HttpResponseMessage responseAsync = await client.GetAsync(url); // sync HttpResponseMessage responseSync = client.GetAsync(url).Result; } 

Puede acceder a la propiedad Result de la tarea, lo que hará que su hilo se bloquee hasta que el resultado esté disponible:

 string code = GenerateCodeAsync().Result; 

Nota: En algunos casos, esto puede llevar a un interlocking: su llamada a Result bloquea el hilo principal, impidiendo así que se ejecute el rest del código asincrónico. Tiene las siguientes opciones para asegurarse de que esto no suceda:

  • Agregue .ConfigureAwait(false) a su método de biblioteca o
  • Ejecute explícitamente su método async en un hilo del grupo de subprocesos y espere a que finalice:

     string code = Task.Run(GenerateCodeAsync).Result; 

Debe obtener el awaiter ( GetAwaiter() ) y finalizar la espera para completar la tarea asincrónica ( GetResult() ).

 string code = GenerateCodeAsync().GetAwaiter().GetResult(); 

Debería poder hacer esto usando delegates, expresión lambda

 private void button2_Click(object sender, EventArgs e) { label1.Text = "waiting...."; Task sCode = Task.Run(async () => { string msg =await GenerateCodeAsync(); return msg; }); label1.Text += sCode.Result; } private Task GenerateCodeAsync() { return Task.Run(() => GenerateCode()); } private string GenerateCode() { Thread.Sleep(2000); return "I m back" ; } 

Necesito llamar a este método desde un método sincrónico.

Es posible con GenerateCodeAsync().Result o GenerateCodeAsync().Wait() , como sugiere la otra respuesta. Esto bloquearía el hilo actual hasta que se complete GenerateCodeAsync .

Sin embargo, su pregunta está etiquetada con asp.net , y también dejó el comentario:

Esperaba una solución más simple, pensando que asp.net manejó esto mucho más fácil que escribir tantas líneas de código

Mi punto es que no deberías estar bloqueando un método asincrónico en ASP.NET. Esto reducirá la escalabilidad de su aplicación web y puede crear un punto muerto (cuando se await una continuación dentro de GenerateCodeAsync en AspNetSynchronizationContext ). El uso de Task.Run(...).Result de descargar algo a un subproceso del grupo y luego bloquear afectará aún más la escalabilidad, ya que incurre en +1 subproceso más para procesar una solicitud HTTP determinada.

ASP.NET tiene soporte integrado para métodos asincrónicos, ya sea a través de controladores asíncronos (en ASP.NET MVC y API web) o directamente a través de AsyncManager y PageAsyncTask en ASP.NET clásico. Deberías usarlo Para más detalles, verifique esta respuesta .

Microsoft Identity tiene métodos de extensión que llaman a los métodos asincrónicos sincrónicamente. Por ejemplo, hay un método GenerateUserIdentityAsync () e igual CreateIdentity ()

Si miras UserManagerExtensions.CreateIdentity () se verá así:

  public static ClaimsIdentity CreateIdentity(this UserManager manager, TUser user, string authenticationType) where TKey : IEquatable where TUser : class, IUser { if (manager == null) { throw new ArgumentNullException("manager"); } return AsyncHelper.RunSync(() => manager.CreateIdentityAsync(user, authenticationType)); } 

Ahora veamos qué hace AsyncHelper.RunSync

  public static TResult RunSync(Func> func) { var cultureUi = CultureInfo.CurrentUICulture; var culture = CultureInfo.CurrentCulture; return _myTaskFactory.StartNew(() => { Thread.CurrentThread.CurrentCulture = culture; Thread.CurrentThread.CurrentUICulture = cultureUi; return func(); }).Unwrap().GetAwaiter().GetResult(); } 

Entonces, este es su contenedor para el método asíncrono. Y, por favor, no lea los datos de los resultados: potencialmente bloqueará su código en ASP.

Hay otra forma, lo cual es sospechoso para mí, pero también puedes considerarlo

  Result r = null; YourAsyncMethod() .ContinueWith(t => { r = t.Result; }) .Wait();