user2859240 user2859240 - 7 months ago 95
Vb.net Question

Google API No application is associated with the specified file for this operation

I am able to successfully connect to the Google Calendar API and sync events when running locally on my machine. However when the web application runs on the server I get the following error:

No application is associated with the specified file for this operation
at Microsoft.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at Microsoft.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccess(Task task)
at Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.<AuthorizeAsync>d__1.MoveNext()


I don't understand why it is having trouble with file associations since I am not using a FileDataStore but a database to store the data. Everything is functional when using Visual Studio in Debug Mode on the local machine but the error happens when uploaded to the production server.

This is the code I am using to connect to the API:

Private Shared Function BuildService(EmployeeID As String) As Google.Apis.Calendar.v3.CalendarService
'// Google OAuth for User Calendar
Dim credential As UserCredential = GoogleWebAuthorizationBroker.AuthorizeAsync(New ClientSecrets() With { _
.ClientId = GoogleAPI.ClientID, _
.ClientSecret = GoogleAPI.ClientSecret _
}, New String() {Google.Apis.Calendar.v3.CalendarService.Scope.Calendar}, EmployeeID, CancellationToken.None, New GoogleDataStore()).Result

' Create the service.
Dim service = New Google.Apis.Calendar.v3.CalendarService(New BaseClientService.Initializer() With { _
.HttpClientInitializer = credential, _
.ApplicationName = "Influence Calandar App" _
})


Return service
End Function


This is the GoogleDataStore class I am using to implement IDataStore:

Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports Google.Apis.Util.Store
Imports System.Data.SqlClient
Imports System.Threading.Tasks
Imports Google.Apis.Json

Public Class GoogleDataStore
Implements IDataStore

''' <summary>Gets the full folder path.</summary>

Private Property _ConnectionExists() As [Boolean]
Get
Return m__ConnectionExists
End Get
Set(value As [Boolean])
m__ConnectionExists = Value
End Set
End Property
Private m__ConnectionExists As [Boolean]
Public ReadOnly Property connectionExists() As [Boolean]
Get
Return _ConnectionExists
End Get
End Property


''' <summary>
''' Constructs a new file data store with the specified folder. This folder is created (if it doesn't exist
''' yet) under the current directory
''' </summary>
''' <param name="folder">Folder name</param>
Public Sub New()
Dim myConnection As SqlConnection = Me.connectdb()
' Opens a connection to the database.
If _ConnectionExists Then
' check if the Table Exists;
Try
Dim myReader As SqlDataReader = Nothing
Dim myCommand As New SqlCommand("select 1 from GoogleUser where 1 = 0", myConnection)
myReader = myCommand.ExecuteReader()
While myReader.Read()
Dim hold = myReader("Column1")
End While
Catch
' table doesn't exist we create it
Dim myCommand As New SqlCommand("CREATE TABLE [dbo].[GoogleUser]( " + " [username] [nvarchar](4000) NOT NULL," + " [RefreshToken] [nvarchar](4000) NOT NULL," + " [Userid] [nvarchar](4000) NOT NULL" + " ) ON [PRIMARY]", myConnection)
myCommand.ExecuteNonQuery()
End Try
End If

myConnection.Close()
End Sub

''' <summary>
''' Stores the given value for the given key. It creates a new file (named <see cref="GenerateStoredKey"/>) in
''' <see cref="FolderPath"/>.
''' </summary>
''' <typeparam name="T">The type to store in the data store</typeparam>
''' <param name="key">The key</param>
''' <param name="value">The value to store in the data store</param>
Public Function StoreAsync(Of T)(key As String, value As T) As Task Implements IDataStore.StoreAsync
If String.IsNullOrEmpty(key) Then
Throw New ArgumentException("Key MUST have a value")
End If
Dim serialized = NewtonsoftJsonSerializer.Instance.Serialize(value)

Dim myConnection As SqlConnection = Me.connectdb()
If Not _ConnectionExists Then
Throw New Exception("Not connected to the database")
End If

' Try and find the Row in the DB.
Using command As New SqlCommand("select Userid from GoogleUser where UserName = @username", myConnection)
command.Parameters.AddWithValue("@username", key)

Dim hold As String = Nothing
Dim myReader As SqlDataReader = command.ExecuteReader()
While myReader.Read()
hold = myReader("Userid").ToString()
End While
myReader.Close()

If hold Is Nothing Then
Try
' New User we insert it into the database
Dim insertString As String = "INSERT INTO [dbo].[GoogleUser] ([username],[RefreshToken],[Userid]) " + " VALUES (@key,@value,'1' )"

Dim commandins As New SqlCommand(insertString, myConnection)
commandins.Parameters.AddWithValue("@key", key)
commandins.Parameters.AddWithValue("@value", serialized)
commandins.ExecuteNonQuery()
Catch ex As Exception
Throw New Exception("Error inserting new row: " + ex.Message)
End Try
Else
Try
' Existing User We update it
Dim insertString As String = "update [dbo].[GoogleUser] " + " set [RefreshToken] = @value " + " where username = @key"

Dim commandins As New SqlCommand(insertString, myConnection)
commandins.Parameters.AddWithValue("@key", key)
commandins.Parameters.AddWithValue("@value", serialized)
commandins.ExecuteNonQuery()
Catch ex As Exception
Throw New Exception("Error updating user: " + ex.Message)
End Try
End If
End Using

myConnection.Close()
Return TaskEx.Delay(0)
End Function

''' <summary>
''' Deletes the given key. It deletes the <see cref="GenerateStoredKey"/> named file in <see cref="FolderPath"/>.
''' </summary>
''' <param name="key">The key to delete from the data store</param>
Public Function DeleteAsync(Of T)(key As String) As Task Implements IDataStore.DeleteAsync
If String.IsNullOrEmpty(key) Then
Throw New ArgumentException("Key MUST have a value")
End If
Dim myConnection As SqlConnection = Me.connectdb()
If Not _ConnectionExists Then
Throw New Exception("Not connected to the database")
End If

' Deletes the users data.
Dim deleteString As String = "delete [dbo].[GoogleUser] from " + " where username = @key"
Dim commandins As New SqlCommand(deleteString, myConnection)
commandins.Parameters.AddWithValue("@key", key)
commandins.ExecuteNonQuery()

myConnection.Close()
Return TaskEx.Delay(0)
End Function

''' <summary>
''' Returns the stored value for the given key or <c>null</c> if the matching file (<see cref="GenerateStoredKey"/>
''' in <see cref="FolderPath"/> doesn't exist.
''' </summary>
''' <typeparam name="T">The type to retrieve</typeparam>
''' <param name="key">The key to retrieve from the data store</param>
''' <returns>The stored object</returns>
Public Function GetAsync(Of T)(key As String) As Task(Of T) Implements IDataStore.GetAsync
'Key is the user string sent with AuthorizeAsync
If String.IsNullOrEmpty(key) Then
Throw New ArgumentException("Key MUST have a value")
End If
Dim tcs As New TaskCompletionSource(Of T)()

' Note: create a method for opening the connection.
Dim myConnection As SqlConnection = Me.connectdb()

' Try and find the Row in the DB.
Using command As New SqlCommand("select RefreshToken from GoogleUser where UserName = @username;", myConnection)
command.Parameters.AddWithValue("@username", key)

Dim RefreshToken As String = Nothing
Dim myReader As SqlDataReader = command.ExecuteReader()
While myReader.Read()
RefreshToken = myReader("RefreshToken").ToString()
End While

If RefreshToken Is Nothing Then
' we don't have a record so we request it of the user.
tcs.SetResult(Nothing)
Else

Try
' we have it we use that.
tcs.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize(Of T)(RefreshToken))
Catch ex As Exception
tcs.SetException(ex)

End Try
End If
End Using

Return tcs.Task
End Function

''' <summary>
''' Clears all values in the data store. This method deletes all files in <see cref="FolderPath"/>.
''' </summary>
Public Function ClearAsync() As Task Implements IDataStore.ClearAsync
Dim myConnection As SqlConnection = Me.connectdb()
If Not _ConnectionExists Then
Throw New Exception("Not connected to the database")
End If

' Removes all data from the Table.
Dim truncateString As String = "truncate table [dbo].[GoogleUser] "
Dim commandins As New SqlCommand(truncateString, myConnection)
commandins.ExecuteNonQuery()

myConnection.Close()
Return TaskEx.Delay(0)
End Function

''' <summary>Creates a unique stored key based on the key and the class type.</summary>
''' <param name="key">The object key</param>
''' <param name="t">The type to store or retrieve</param>
Public Shared Function GenerateStoredKey(key As String, t As Type) As String
Return String.Format("{0}-{1}", t.FullName, key)
End Function

'Handel's creating the connection to the database
Private Function connectdb() As SqlConnection
Dim myConnection As SqlConnection = Nothing
Try
myConnection = New SqlConnection(ConfigurationManager.ConnectionStrings("db1251ConnectionString").ConnectionString)
Try
myConnection.Open()
' ensuring that we are able to make a connection to the database.
If myConnection.State = System.Data.ConnectionState.Open Then
_ConnectionExists = True
Else
Throw New ArgumentException("Error unable to open connection to the database.")
End If
Catch ex As Exception

Throw New ArgumentException("Error opening Connection to the database: " + ex.Message)

End Try
Catch ex As Exception

Throw New ArgumentException("Error creating Database Connection: " + ex.Message)
End Try

Return myConnection
End Function

End Class


Any help would be appreciated. I have been trying to figure out the error but have had no luck so far. None of the other posts on "No application is associated with the specified file for this operation" have been helpful so far since I am not trying to access files. Is "GoogleWebAuthorizationBroker" trying to open a file despite being told to use the GoogleDataStore class instead of the FileDataStore, and if so how do I stop it?

I am running a vb.net web application hosted on a VM with Windows Azure.

Answer

I was finally able to get it working by switching from GoogleWebAuthorizationBroker to GoogleAuthorizationCodeFlow for authentication.

Hopefully the code below will help you if you get the same error.

Private Sub GetGoogleService()
    Dim datafolder As String = Server.MapPath("App_Data/CalendarService.api.auth.store")
    Dim scopes As IList(Of String) = New List(Of String)()
    Dim UserId As String = aMP.currentEmployeeID 

    scopes.Add(Google.Apis.Calendar.v3.CalendarService.Scope.Calendar)
    Dim myclientsecret As New ClientSecrets() With { _
      .ClientId = GoogleAPI.ClientID, _
      .ClientSecret = GoogleAPI.ClientSecret _
    }

    Dim flow As GoogleAuthorizationCodeFlow

    flow = New GoogleAuthorizationCodeFlow(New GoogleAuthorizationCodeFlow.Initializer() With { _
      .DataStore = New FileDataStore(datafolder), _
      .ClientSecrets = myclientsecret, _
      .Scopes = scopes _
    })

    Dim uri As String = Request.Url.ToString()

    Dim code = Request("code")
    If code IsNot Nothing Then
        Dim token = flow.ExchangeCodeForTokenAsync(UserId, code, uri.Substring(0, uri.IndexOf("?")), CancellationToken.None).Result

        ' Extract the right state.
        Dim oauthState = AuthWebUtility.ExtracRedirectFromState(flow.DataStore, UserId, Request("state")).Result
        Response.Redirect(oauthState)
    Else
        Dim result = New AuthorizationCodeWebApp(flow, uri, uri).AuthorizeAsync(UserId, CancellationToken.None).Result
        If result.RedirectUri IsNot Nothing Then
            ' Redirect the user to the authorization server.
            Response.Redirect(result.RedirectUri)
        Else
            ' The data store contains the user credential, so the user has been already authenticated.
            'Response.Write("User Already Authorized")
            Me.isConnected = New Google.Apis.Calendar.v3.CalendarService(New BaseClientService.Initializer() With { _
               .HttpClientInitializer = result.Credential, _
               .ApplicationName = "Calandar App" _
           })
        End If
    End If

End Sub