I'm sure there's some other threads about this, but i think i need a threading for dummies or something.
My problem: I want to fetch a value by a WebRequest and display it. My code looks something like this:
Foo = New Fetcher()
AddHandler Foo.HasResult, AddressOf Me.FetchValue
Private Sub FetchValue()
If Foo.HasErrors Then
MyTextBlock.Text = "ERROR"
Exit Sub
End IF
MyTextBlock.Text = Foo.Value 'Here it crashes.....
End sub
Public Class Fetcher
Public Event HasResult(ByVal F As Fetcher)
Public WasError As Boolean = True
Public Value As String = ""
Public Sub New()
Dim request As WebRequest = WebRequest.Create("theurl")
request.BeginGetResponse(New AsyncCallback(AddressOf Me.GetValueAnswer), request)
End Sub
Private Sub GetValueAnswer(asynchronousResult As IAsyncResult)
Dim request As HttpWebRequest = asynchronousResult.AsyncState
If Not request Is Nothing Then
Try
Dim response As WebResponse = request.EndGetResponse(asynchronousResult)
Using stream As Stream = response.GetResponseStream()
Using reader As New StreamReader(stream, System.Text.Encoding.UTF8)
Dim responseString = reader.ReadToEnd()
Me.Value = ResponseString
Me.WasError = False
End Using
End Using
Catch(Exception Ex)
Me.WasError = True 'Not needed in this example, i know...
End Try
End If
RaiseEvent HasResult(Me)
End Sub
End Class
This is a little bit simplified, but it's the same error as well.
At the line with the comment "Here it crashes....." i get an exception with "The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))"
I can how ever see that my result is fetched when i explore the Foo.
So, how is the right way to do this?
(And yes; if I enter a bad URL or something so that "WasError" is true, of course i get the same exception when i try to set my textblock to "ERROR")
EDIT: After some really strong words, blood sweat and tears, i came up with this change to the FetchValue(), and now it finally works....
If Me.MyTextBlock.Dispatcher.HasThreadAccess Then
If Foo.HasErrors Then
MyTextBlock.Text = "ERROR"
Exit Sub
End IF
MyTextBlock.Text = Foo.Value
Else
Me.MyTestBlock.Dispatcher.RunAsync(Core.CoreDispatcherPriority.Normal, _
AddressOf Me.FetchValue)
End If
I do how ever get a warning on the row in else that says "Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the Await operator to the result of the call."
Any ideas for how to make this warning go away?
It's far, far easier to do this with HttpClient and Async/Await.
My VB is rusty, but here goes:
Public Class Fetcher
Public Result As Task(of String)
Public Sub New()
Dim client As HttpClient = New HttpClient()
Result = client.GetStringAsync("theurl")
End Sub
End Class
Usage:
Foo = New Fetcher()
Try
Dim data As String = Await Foo.Result
MyTextBlock.Text = data
Catch(Exception Ex)
MyTextBlock.Text = "ERROR"
End Try
Note that the Task<T> type handles return values, error conditions, and completion notification. So all the posted code in your Fetcher class is pretty much unnecessary if you use HttpClient with Task<T>. If your Fetcher class doesn't do anything else, then you should probably just remove it entirely:
Dim client As HttpClient = New HttpClient()
Try
MyTextBlock.Text = Await client.GetStringAsync("theurl")
Catch(Exception Ex)
MyTextBlock.Text = "ERROR"
End Try
Related
I'm trying to write code to read files in a OneDrive folder of mine and, in the future, create, move and delete them.
The scrap of code below works, but it requests a new authentication (shows Microsoft's OAuth window) for each iteration in the For Each ... Next.
What am I doing wrong here?
Imports Microsoft.Graph
Public Class FormGraphClient
Private Shared client As GraphServiceClient
Private Async Sub FormGraphClient_Load(sender As Object, e As EventArgs) Handles MyBase.Load
client = AuthenticationHelper.GetAuthenticatedClient
Dim formsmonitor_items = Await client.Me.Drive.Root.ItemWithPath("FormsMonitor").Children.Request.GetAsync
Dim forms As New Dictionary(Of String, String)
For Each form In formsmonitor_items
If form.Name Like "*.json" Then
Using formstream = Await client.Me.Drive.Items.Item(form.Id).Content.Request.GetAsync
Using reader = New IO.StreamReader(formstream)
forms(form.Name) = reader.ReadToEnd
End Using
End Using
End If
Next
End Sub
End Class
I'm using also this helper class:
Imports System.Net.Http.Headers
Imports Microsoft.Graph
Imports Microsoft.Identity.Client
Public Class AuthenticationHelper
Shared clientId As String = "my-client-id"
Public Shared Scopes As String() = {"User.Read", "Files.ReadWrite.All"}
Public Shared IdentityClientApp As PublicClientApplication = New PublicClientApplication(clientId)
Public Shared TokenForUser As String = Nothing
Public Shared Expiration As DateTimeOffset
Private Shared graphClient As GraphServiceClient = Nothing
Public Shared Function GetAuthenticatedClient() As GraphServiceClient
If graphClient Is Nothing Then
Try
graphClient = New GraphServiceClient(
"https://graph.microsoft.com/v1.0",
New DelegateAuthenticationProvider(
Async Function(requestMessage)
Dim token = Await GetTokenForUserAsync()
requestMessage.Headers.Authorization = New AuthenticationHeaderValue("bearer", token)
requestMessage.Headers.Add("SampleID", "uwp-csharp-apibrowser-sample")
End Function))
Return graphClient
Catch ex As Exception
Debug.WriteLine("Could not create a graph client: " & ex.Message)
End Try
End If
Return graphClient
End Function
Public Shared Async Function GetTokenForUserAsync() As Task(Of String)
Dim authResult As AuthenticationResult
Dim ex As Exception = Nothing
Try
authResult = Await IdentityClientApp.AcquireTokenSilentAsync(Scopes, IdentityClientApp.GetUser("antonio.patricio#agu.gov.br"))
TokenForUser = authResult.AccessToken
Catch ex
If TokenForUser Is Nothing OrElse Expiration <= DateTimeOffset.UtcNow.AddMinutes(5) Then
End If
End Try
If ex IsNot Nothing Then
Try
authResult = Await IdentityClientApp.AcquireTokenAsync(Scopes)
TokenForUser = authResult.AccessToken
Expiration = authResult.ExpiresOn
Catch ex
End Try
End If
Return TokenForUser
End Function
End Class
I checked up samples here and there, and came up with this wrapper class. I think I'll work on it more, but for the moment, it solved the problem mentioned at this post:
Imports System.Net.Http
Imports System.Net.Http.Headers
Imports Microsoft.Graph
Imports Microsoft.Identity.Client
Public Class MsGraph
Private Const baseUrl As String = "https://graph.microsoft.com/v1.0"
Private ReadOnly client_id As String
Private ReadOnly scopes As String()
Private authSuccess As Boolean
Private clientApp As PublicClientApplication
Public Sub New(app_client_id As String, ParamArray app_scopes As String())
client_id = app_client_id
If Not app_scopes.Contains("User.Read", StringComparer.InvariantCultureIgnoreCase) Then
app_scopes = {"User.Read"}.Concat(app_scopes).ToArray
End If
scopes = app_scopes
clientApp = New PublicClientApplication(client_id)
Dim authProvider = New DelegateAuthenticationProvider(AddressOf AuthenticateRequestAsync)
Try
Client = New GraphServiceClient(baseUrl, authProvider)
Catch ex As Exception
Stop
End Try
End Sub
Public ReadOnly Property Client As GraphServiceClient
Public Async Function AuthenticateRequestAsync(request As HttpRequestMessage) As Task
request.Headers.Authorization = New AuthenticationHeaderValue("bearer", Await GetTokenForUserAsync())
End Function
Private Async Function GetTokenForUserAsync() As Task(Of String)
Dim success As Boolean
Dim authResult As AuthenticationResult = Nothing
If clientApp.Users.Any Then
Try
authResult = Await clientApp.AcquireTokenSilentAsync(scopes, clientApp.Users.FirstOrDefault())
success = True
Catch ex As Exception
Stop
End Try
Else
Try
authResult = Await clientApp.AcquireTokenAsync(scopes)
success = True
Catch ex As Exception
Stop
End Try
End If
authSuccess = success
Return authResult?.AccessToken
End Function
End Class
I'm trying to raise an event from a UWP class library background task, but I need to marshal it on the main UI thread in the UWP app I plan to use the class library with. I'm looking for a way to marshal it from the background task though. I've looked through a couple of articles including this one:
Raise Events in .NET on the main UI thread
but its not making sense to me. Can anyone help me with the below code?
Private ReceiveTask As New Task(Sub()
Dim InStream As Stream = CSocket.InputStream.AsStreamForRead
While killswitch = False
Try
Dim Reader As StreamReader = New StreamReader(InStream)
Dim DataText As String = ""
While Reader.Peek <> -1
DataText &= Convert.ToChar(Reader.Read)
End While
RaiseEvent DataReceived(DataText)
Catch ex As Exception
RaiseEvent SocketError("Receiving", ex.Message)
End Try
End While
End Sub)
Thanks to jmcilhinney, the below code works!
Private ReceiveTask As New Task(Sub()
Dim InStream As Stream = CSocket.InputStream.AsStreamForRead
While killswitch = False
Try
Dim Reader As StreamReader = New StreamReader(InStream)
If Reader.Peek <> -1 Then
DataText = ""
While Reader.Peek <> -1
DataText &= Convert.ToChar(Reader.Read)
End While
uiContext.Post(AddressOf RaiseDataToUI, Nothing)
End If
Catch ex As Exception
ReceiveError = ex.Message
uiContext.Post(AddressOf RaiseErrorToUI, Nothing)
End Try
End While
End Sub)
I've not done any UWP development myself but I believe the correct way to do as you want is to use the SynchronizationContext class, as in WPF. The principle is that the SynchronizationContext.Current property will yield a thread-specific instance of the class, so you can get the value of that property on the UI thread and then use that object elsewhere to marshal a call to the owning thread. The property value is usually retrieved in the constructor of an object that is itself created on the UI thread, e.g.
Imports System.Threading
Public Class SomeClass
'Get the context for the thread on which the current instance is created.
Private uiContext As SynchronizationContext = SynchronizationContext.Current
Private Sub ThisMethodIsCalledOnASecondaryThread()
uiContext.Post(AddressOf ThisMethodIsCalledOnTheUIThread, Nothing)
End Sub
Private Sub ThisMethodIsCalledOnTheUIThread(data As Object)
'Execute UI thread logic here, e.g. raise an event.
End Sub
End Class
I'm trying to call a web service and have my code wait for that service to return a result (or timeout). My project is Silverlight 5 with Web Services using .NET 4.0 and I'm running this project under VS 2012 with the Microsoft.Bcl.Async.1.0.16\lib\sl4\Microsoft.Threading.Tasks.dll ... Task.Extensions.dll ... and Extensions.Silverlight.dll.
This is how I've been doing it and it's working, but I'm trying to figure out how to change my code so that I can use the Async/Await process. The web service reference is configured to return ObservableCollection and Generic.Dictionary with Reuse types in all referenced assemblies.
Some of my code I need to convert to Async/Await:
Private _Units As Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units)
Public Property Units() As Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units)
Get
Return _Units
End Get
Set(ByVal value As Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units))
_Units = value
OnPropertyChanged(New PropertyChangedEventArgs("Units"))
End Set
End Property
Public Sub ReadUnits()
Try
' Client is required
If Not Me.Client Is Nothing Then
' User is required
If Not Me.User Is Nothing Then
' Must be a real Client
If Me.Client.ClientID > 0 Then
' My have a sites
If Not Me.Site Is Nothing Then
' Call the web service relative to where this application is running
Dim webServiceURI As New Uri("../WebServices/Unit.svc", UriKind.RelativeOrAbsolute)
Dim webServiceAddress As New EndpointAddress(webServiceURI)
' Setup web Service proxy
Dim wsUnits As New DC.SL.Services.WebServiceUnit.UnitClient
wsUnits.Endpoint.Address = webServiceAddress
' Add event handler so we can trap for web service completion
AddHandler wsUnits.LoadsCompleted, AddressOf LoadUnitsCompleted
' Call web service to get Sites the user has access to
wsUnits.LoadsAsync(Me.Client, Me.Site.SiteID, Me.Size.SizeID, Me.RentalType.RentalTypeID, Me.UnitState)
End If
End If
End If
End If
Catch ex As Exception
Dim Problem As New DC.SL.Tools.Errors(ex)
End Try
End Sub
Private Sub LoadUnitsCompleted(ByVal sender As Object, ByVal e As DC.SL.Services.WebServiceUnit.LoadsCompletedEventArgs)
Try
If Not IsNothing(e.Result) Then
Me.Units = e.Result
If Me.Units.Count > 0 Then
Me.Unit = Me.Units.Item(0)
End If
End If
Catch ex As Exception
Dim Problem As New DC.SL.Tools.Errors(ex)
End Try
End Sub
Still not getting this to work ... here is what I have now, but the problem remains ... UI thread execution continues and does NOT wait for the Web Service call to finish.
Calling code:
ReadUnitsAsync().Wait(3000)
Here is the updated code:
Public Async Function ReadUnitsAsync() As Task(Of Boolean)
Dim Results As Object = Await LoadReadUnitsAsync()
Return True
End Function
Public Function LoadReadUnitsAsync() As Task(Of System.Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units))
LoadReadUnitsAsync = Nothing
Dim tcs = New TaskCompletionSource(Of System.Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units))
' Client is required
If Not Me.Client Is Nothing Then
' User is required
If Not Me.User Is Nothing Then
' Must be a real Client associated
If Me.Client.ClientID > 0 Then
' Only get associated sites IF we don't have any defined
If Not Me.Site Is Nothing Then
' Call the web service relative to where this application is running
Dim webServiceURI As New Uri("../WebServices/Unit.svc", UriKind.RelativeOrAbsolute)
Dim webServiceAddress As New EndpointAddress(webServiceURI)
' Setup Site web Service proxy
Dim wsUnits As New DC.SL.Services.WebServiceUnit.UnitClient
wsUnits.Endpoint.Address = webServiceAddress
' Add event handler so we can trap for web service completion
AddHandler wsUnits.LoadUnitsCompleted, Sub(s, e)
If e.Error IsNot Nothing Then
tcs.TrySetException(e.Error)
ElseIf e.Cancelled Then
tcs.TrySetCanceled()
Else
tcs.TrySetResult(e.Result)
End If
End Sub
'' Set Busy Status
'BusyStack.Manage(ProcessManager.StackAction.Add, "ReadUnits", Me.IsWorking, Me.IsWorkingMessage)
' Call web service to get Sites the user has access to
wsUnits.LoadUnitsAsync(Me.Client, Me.Site.SiteID, Me.Size.SizeID, Me.RentalType.RentalTypeID, Me.UnitState)
Return tcs.Task
End If
End If
End If
End If
End Function
So here is the final code (abbreviated) that seems to be working to my goals (aka waiting for a Web Service to finish before proceeding).
Public Class UIUnits
Implements INotifyPropertyChanged, IDataErrorInfo
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Public Async Sub OnPropertyChanged(ByVal e As PropertyChangedEventArgs)
Dim propertyEventHandler As PropertyChangedEventHandler = PropertyChangedEvent
Try
If propertyEventHandler IsNot Nothing Then
RaiseEvent PropertyChanged(Me, e)
Select Case e.PropertyName
Case "Size"
Await ReadUnitsAsync()
DoSomethingElseAfterWebServiceCallCompletes()
Case Else
End Select
End If
Catch ex As Exception
Dim problem As New DC.SL.Tools.Errors(ex)
End Try
End Sub
...
Private _Units As Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units)
Public Property Units() As Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units)
Get
Return _Units
End Get
Set(ByVal value As Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units))
_Units = value
OnPropertyChanged(New PropertyChangedEventArgs("Units"))
End Set
End Property
...
Public Async Function ReadUnitsAsync() As Task(Of Boolean)
Me.Units = Await LoadReadUnitsAsync()
Return True
End Function
...
Public Function LoadReadUnitsAsync() As Task(Of System.Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units))
LoadReadUnitsAsync = Nothing
Dim tcs = New TaskCompletionSource(Of System.Collections.ObjectModel.ObservableCollection(Of DC.SL.Services.WebServiceUnit.Units))
' Client is required
If Not Me.Client Is Nothing Then
' User is required
If Not Me.User Is Nothing Then
' Must be a real Client associated
If Me.Client.ClientID > 0 Then
' Only get associated sites IF we don't have any defined
If Not Me.Site Is Nothing Then
' Call the web service relative to where this application is running
Dim webServiceURI As New Uri("../WebServices/Unit.svc", UriKind.RelativeOrAbsolute)
Dim webServiceAddress As New EndpointAddress(webServiceURI)
' Setup web Service proxy
Dim wsUnits As New DC.SL.Services.WebServiceUnit.UnitClient
wsUnits.Endpoint.Address = webServiceAddress
' Add event handler so we can trap for web service completion
AddHandler wsUnits.LoadUnitsCompleted, Sub(s, e)
If e.Error IsNot Nothing Then
tcs.TrySetException(e.Error)
ElseIf e.Cancelled Then
tcs.TrySetCanceled()
Else
tcs.TrySetResult(e.Result)
End If
End Sub
' Call web service
wsUnits.LoadUnitsAsync(Me.Client, Me.Site.SiteID, Me.Size.SizeID, Me.RentalType.RentalTypeID, Me.UnitState)
Return tcs.Task
End If
End If
End If
End If
End Function
In this case it's probably easiest to convert from the lowest level and work your way up. First, you need to define your own TAP-friendly extension methods on your service. VS will generate these for you if you are doing desktop development, but unfortunately it will not do this for Silverlight.
The MSDN docs describe how to wrap EAP (EAP is the pattern that uses *Async methods with matching *Completed events). If you have APM methods, it's even easier to wrap those into TAP (APM is the pattern that uses Begin*/End* method pairs).
Once you have a wrapper, e.g., LoadUnitsTaskAsync, change your method to call that instead of LoadUnitsAsync and Await the result. This will require your ReadUnits method to be Async, so change it to a Task-returning Function (and change its name from ReadUnits to ReadUnitsAsync). Next change all callers of ReadUnitsAsync so they Await its result. Repeat until you reach an actual event handler, which may be Async Sub (do not use Async Sub for any intermediate methods; use Async Function ... As Task instead).
I used this library to achieve async await in my Silverlight 5 project
My program is throwing an error I should handle. But I cannot return a string message because the it is a function that returns a dataset:
Public Function getUserInfo(ByValue testUserIdAs String) As DataSet
Dim dsUseInfo As DataSet = New DataSet()
Try
Dim objTestWs As New TestWebService.UserMaintenanceSoapClient
dsUseInfo = objTestWs.dsGetUserInfo(TestOU, PAC, paramUserID)
Return (dsUseInfo)
Catch ex As Exception
' TEST FIX ERROR HANDLING -LIWM Please search how to return custom error. I want to return "userid already exists"
Throw
End Try
I was thinking of putting in:
If error
then return "Error Message"
But I can't return it as type string.
It looks like you don't really know what to do with the exception in getUserInfo and just want to pass it on to the outer function.
Well, here's the great thing about exceptions: They are passed on automatically! There's nothing you need to do, and, in particular, you do not need to use the return value of the function for that. Just don't catch the exception until you know what to do with it.
For example, in your case, just handle the error in the calling function instead:
Remove the error handling code from the called function:
Public Function getUserInfo(ByValue testUserIdAs String) As DataSet
Dim objTestWs As New TestWebService.UserMaintenanceSoapClient
Return objTestWs.dsGetUserInfo(TestOU, PAC, paramUserID)
End Function
and add it to the calling function, i.e., replace
...
Dim dsUserInfo As DataSet
dsUserInfo = getUserInfo()
...
with
...
Dim dsUserInfo As DataSet
Try
dsUserInfo = getUserInfo()
Catch ex As Exception
' Do whatever you want to do in case of an error here
MsgBox("Could not get User Info: " & ex.Message)
Return
End Try
...
One you are familiar with this technique, you can go on to more advanced topics like throwing your own exception, like #Justin suggested. Just make sure to include information about the original cause of the error in your own exception, for example, by copying parts of ex.Message into your own exception message and setting the innerException property.
You could throw a generic exception:
Public Function getUserInfo(ByValue testUserIdAs String) As DataSet
Dim dsUseInfo As DataSet = New DataSet()
Try
Dim objTestWs As New TestWebService.UserMaintenanceSoapClient
dsUseInfo = objTestWs.dsGetUserInfo(TestOU, PAC, paramUserID)
Return (dsUseInfo)
Catch ex As Exception
Throw New Exception("Custom message", ex)
End Try
This will set the message on the exception you catch up the call stack to be "Custom Message" with an inner exception that contains the original exception which was thrown.
Or you could create a custom exception(by inheriting from System.Exception) elsewhere if you want to throw a more expressively named exception(and so you don't have to catch all exceptions when you want to catch this custom type).
Public Class UserInfoNotFoundException
Inherits System.Exception
Public Sub New()
End Sub
Public Sub New(message As String)
MyBase.New(message)
End Sub
Public Sub New(message As String, innerException As Exception)
MyBase.New(message, innerException)
End Sub
End Class
You could then, for example, throw a UserInfoNotFoundException.
I would change the whole approach to exceptions and errors:
Create a new assembly (project) and reference it in your application
Declare new shared events which accept your personal datatype (referenced assembly)
Declare at least one shared function (referenced assembly) which does a RaiseEvent on your new events
Add handlers (main application) for your shared events in which you react accordingly
Call your function from within the main application,
passing your own parameters whenever you need to throw errors /
exceptions
This way you circumvent many programming mistakes and centralize error and exception-handling.
Public Module IDENTIFIERS
Public Enum EvtMsg
ERR_MYERR
ERR_MYERR2
End Enum
Public Enum EvtClass
EXCEPTION
ERR
End Enum
End Module
Public Class Events
Shared Event Err(ByVal code As EvtMsg)
Shared Event Exception(ByRef iEx As Exception)
Public Shared Sub Raise(ByVal iEvtClass As EvtClass, ByVal iMsg As EvtMsg, Optional ByRef iEx As Exception = Nothing)
If Not [Enum].IsDefined(GetType(EvtClass), iEvtClass) Then
Dim ex As New ArgumentOutOfRangeException("unbekannte Event-Klasse '" & iEvtClass.ToString & "' übergeben", "iEvtClass")
RaiseEvent Exception(ex)
End If
If Not [Enum].IsDefined(GetType(EvtMsg), iMsg) Then
Dim ex As New ArgumentOutOfRangeException("unbekannte Event-Msg '" & iMsg.ToString & "' übergeben", "iMsg")
RaiseEvent Exception(ex)
End If
Select Case iEvtClass
Case EvtClass.ERR
RaiseEvent Err(iMsg)
Case EvtClass.EXCEPTION
If iEx IsNot Nothing Then
RaiseEvent Exception(iEx)
Else
Dim ex As New MissingFieldException("Raise() ohne Exception aufgerufen, iMsg : " & iMsg & "EvtClass : " & iEvtClass.ToString(), "iEx")
RaiseEvent Exception(ex)
End If
End Select
End Sub
End Class
And now you can easily use these error-handlers in any assembly which references your error-assembly:
Constructor
AddHandler Events.Err, AddressOf Err
AddHandler Events.Exception, AddressOf Except
Class-body
Private Sub Except(ByRef iEx As Exception)
'do your stuff here
End Sub
Private Sub Err(ByVal Err As EvtMsg)
'do your stuff here
End Sub
I have code that will be for example
Dim cmd As New SqlClient.SqlCommand(commandString, databaseString)
Dim result As Integer
Try
result = cmd.ExecuteScalar()
Catch e as Exception
'catch exception code
End Try
Instead of doing that, can I override the ExecuteScalar function to do some kind of generic exception catching?
No, but you can create a generic DoExecuteScalar helper function that takes a SQL string and connection string:
Public Function DoExecuteScalar(commandString as String, databaseString as String) as Object
Dim cmd as New SqlClient.SqlCommand(commandString, databaseString)
Dim result as Integer
Try
result = cmd.ExecuteScalar()
Catch e As Exception
Return 0
End Try
Return result
End Function
No you cannot override methods because you cannot inherit from it since SqlCommand is sealed.
What's wrong with catching (or throwing) exceptions where you're using the SqlCommand?
You could also use composition:
Public Class SqlCommandManager
Private _sqlCommand As System.Data.SqlClient.SqlCommand
Public Sub New(command As System.Data.SqlClient.SqlCommand)
If command Is Nothing Then
Throw New ArgumentNullException("command")
End If
_sqlCommand = command
End Sub
Private ReadOnly Property Command As System.Data.SqlClient.SqlCommand
Get
Return _sqlCommand
End Get
End Property
Public Function ExecuteScalar() As Object
Dim result As Object
Try
result = Command.ExecuteScalar()
Catch e As Exception
'catch exception code
End Try
Return result
End Function
End Class
Not saying this is the best alternative, just that it is one ....