Como escrever um Sub-in Async VB.NET?

Public Class LoginManager
    Implements ILoginManager
    Private ReadOnly _iLoginRepository As ILoginRepository
    Public Sub New()
        _iLoginRepository = New LoginRepository()
    End Sub

    Public Async Sub InsertFailedLoginAttempt(failedLoginAttempt As FailedLogin) Implements ILoginManager.InsertFailedLoginAttempt
        'Example of the S in Solid (Single Repsonsibilty)
        'Need to call these method async. But await errors 
            _iLoginRepository.InsertFailedLoginAttemptAsync(failedLoginAttempt)
            _iLoginRepository.InsertFailedLoginAttemptIntoLoginMasterAsync(failedLoginAttempt)
        End Sub
    End Class

Interface De Repsório:

Public Interface ILoginRepository
    Function IsUserAuthenticatedAsync(ByVal cID As String, ByVal password As String, ByVal IsExternalUser As Boolean) As Task(Of Boolean)
    Sub InsertFailedLoginAttemptAsync(ByVal failedLoginAttempt As FailedLogin)
    Sub InsertFailedLoginAttemptIntoLoginMasterAsync(ByVal failedLoginAttempt As FailedLogin)

End Interface

Implementação Do Repositório:

Public Class LoginRepository
    Implements ILoginRepository
    Public ReadOnly _applicationDBContext As New ApplicationDBContext()

    Public Async Sub InsertFailedLoginAttemptAsync(failedLoginAttempt As FailedLogin) Implements ILoginRepository.InsertFailedLoginAttemptAsync
        Using _applicationDBContext
            _applicationDBContext.RepFailedLogins.Add(failedLoginAttempt)
            Await _applicationDBContext.SaveChangesAsync()
        End Using
    End Sub

    Public Async Sub InsertFailedLoginAttemptIntoLoginMasterAsync(failedLoginAttempt As FailedLogin) Implements ILoginRepository.InsertFailedLoginAttemptIntoLoginMasterAsync
        Using _applicationDBContext
            _applicationDBContext.RepFailedLoginMasters.Add(failedLoginAttempt)
            Await _applicationDBContext.SaveChangesAsync()
        End Using
    End Sub

    ''' <summary>
    ''' Determine whether a user is authenticated, be it an internal or external user
    ''' I have condensed two methods into one
    ''' </summary>
    ''' <param name="cID"></param>
    ''' <param name="password"></param>
    ''' <param name="IsExternalUser"></param>
    ''' <returns></returns>
    Public Async Function IsUserAuthenticatedAsync(cID As String, password As String, IsExternalUser As Boolean) As Task(Of Boolean) Implements ILoginRepository.IsUserAuthenticatedAsync
        If (IsExternalUser And String.IsNullOrEmpty(password)) Then
            Throw New ArgumentNullException("External user requires password")
        End If

        Dim user As Chaser
        Dim toRet As Boolean

        Using _applicationDBContext
            'Two ways to use LINQ
            'First is LINQ Lambda sybntax(little harder to read)
            user = Await _applicationDBContext.Chasers.Where(Function(x) x.CID = cID).FirstOrDefaultAsync()

            'Second is LINQ Query syntax(looks more like SQL just more verbose
            'user = From x In _applicationDBContext.Chasers
            '       Where x.CID = cID
            '       Select x
        End Using

        If IsNothing(user) Then
            toRet = False
        ElseIf Not IsExternalUser And Not IsNothing(user) Then
            toRet = True
        ElseIf IsExternalUser And user.Hash_Password = password Then
            toRet = True
        End If

        Return toRet
    End Function
End Class
Estou a tentar ligar para o método do repositório no meu gestor. É um método async, mas não posso esperar pelo método. Como posso tornar este método awaitable?

Eu acredito que tem algo a ver com a interface e não torná-lo um método async como em C#, Mas eu sou incapaz de fazer isso.

Author: Nkosi, 2017-08-24

2 answers

Subs não deve ser async. Os responsáveis por eventos são a única excepção a essa regra. Você espera Task que só pode ser devolvido de um Function. Se a intenção é fazer essa interface async então todos os membros precisam ser funções que retornem a Task ou sua derivada.

Async é algo que borbulha até ao fim quando usado. Dito isto, o ILoginManager juntamente com o ILoginRepository deve ser refactorado (se possível) para seguir a sintaxe adequada.

Referência: Async / Wait - Melhores práticas na programação assíncrona

 2
Author: Nkosi, 2017-08-24 20:48:14

Fixado através da resposta de Nkosi:

Interface:

Public Interface ILoginRepository
    Function IsUserAuthenticatedAsync(ByVal cID As String, ByVal password As String, ByVal IsExternalUser As Boolean) As Task(Of Boolean)
    Function InsertFailedLoginAttemptAsync(ByVal failedLoginAttempt As FailedLogin) As Task
    Function InsertFailedLoginAttemptIntoLoginMasterAsync(ByVal failedLoginAttempt As FailedLogin) As Task

End Interface

Método de Gestão:

 Public Async Function InsertFailedLoginAttempt(failedLoginAttempt As FailedLogin) As Task Implements ILoginManager.InsertFailedLoginAttempt
        'Example of the S in Solid (Single Repsonsibilty)
        Await _iLoginRepository.InsertFailedLoginAttemptAsync(failedLoginAttempt)
        Await _iLoginRepository.InsertFailedLoginAttemptIntoLoginMasterAsync(failedLoginAttempt)
    End Function
 2
Author: Andrew, 2017-08-24 20:48:05