1

CallByName and Invoke are not async functions so I can't call them asynchronously using the obvious syntax like:

rv = await CallByName(objScripts, txtScript.Text, CallType.Method)

Apparently I am the only one to ever need this so no answers on the internet.

My use case is I have hundreds of database transformation scripts written in vb.net. I have a database of which script processes which database. My code then reads the database and processes each script as the updated databases arrive, or I can select the script from a dropdown in my UI and run it that way.

I now need to run some of theses scripts async so I can use PuppeteerSharp to scrape some data rather use a database.

So how do I make that work?

Charlieface
  • 52,284
  • 6
  • 19
  • 43
Brad Mathews
  • 1,567
  • 2
  • 23
  • 45

2 Answers2

2

Just cast the result to Task and await it

rv = Await DirectCast(CallByName(objScripts, txtScript.Text, CallType.Method), Task)
Charlieface
  • 52,284
  • 6
  • 19
  • 43
0

Ok got it figure out.

Private Async Function RunAScript_Worker(ScriptName As String, objScript As Object) As Task(Of Integer)

    Try
        Dim result As Task(Of Integer) = CType(objScript.GetType().GetMethod(ScriptName).Invoke(objScript, Nothing), Task(Of Integer))
        Await result
        Return result.Result
    Catch ex As Exception
        Throw ex
    End Try

End Function

And you call it like this:

rv = Await RunAScript_Worker(ScriptName, ClsContainingMethod)

This uses reflection and .Invoke and by adding Task(Of Integer) type turns it into an awaitable method.

You also need to add Async the the called method, but you can leave the ones the don't need it alone and it will work fine.

Edit:

Well, almost there. The problem arises when I try to return a value from the other functions that are not set to async. The await expects a Task(of Integer) but the non-async function return just Integer and I get a run time error.

So I figured out how to use reflection do determine if the method was async. I discovered the name paramerer in GetMethod() is case sensitive so for that and other reasons I added check before executing the method. I also like @Charlieface's solution better than mine so:

If Not objScripts.GetType().GetMethod(ScriptName) Is Nothing Then
    If objScripts.GetType().GetMethod(ScriptName).ReturnTypeCustomAttributes.ToString.Contains("Task") Then
        Return Await DirectCast(CallByName(objScripts, ScriptName, CallType.Method), Task(Of Integer))
    Else
        Return CallByName(objScripts, ScriptName, CallType.Method)
    End If
Else
    ' Exception handling
End If
Brad Mathews
  • 1,567
  • 2
  • 23
  • 45
  • 3
    `Catch ex As Exception Throw ex` just no. That will wipe the stack trace. You could do just `Throw` or better yet don't catch it at all if you intend to do nothing with it – Charlieface Mar 07 '22 at 20:42
  • That explains a lot! I always do a Throw so I can set a breakpoint and debug in the function that generates the error, but I never knew why the stacktrace got cleared! – Brad Mathews Mar 07 '22 at 23:57
  • A simple `throw` (without the `ex`) will rethrow the exception maintaining the original stack trace (with a minor exception that isn't relevant for most cases). You would still be able to set a breakpoint on it. But you might also want to consider turning up your break-on-throw exception settings. By default, the debugger will only break if most exceptions are uncaught, but you can change this in Debug - Windows - Exception Settings. – Craig Mar 08 '22 at 14:02
  • @Craig, I have played with the Exception Settings now and again in the past and I cannot find the right balance. So what works for me and my workflow is adding Debug.Print(ex.message), an error logger and a throw so I have available the most effective debugging technique for that particular situation. I go back a few decades to On Error Goto and frankly, I still prefer it to try/catch much of the time (when not in C# of course) as I do a lot of Edit and Continue style development - old dogs and new tricks or something like that ;-). – Brad Mathews Mar 08 '22 at 18:01
  • A well-written `On Error GoTo Handler` isn't really much different from try/catch. I find that my default is not to have any try/catch statements (aside from the moral equivalent at a top level to catch anything unhandled and try to give something slightly less user-hostile than the default behavior). They only get added for specific situations where I expect something non-fatal. – Craig Mar 08 '22 at 20:02
  • But I agree that getting the exception settings right can be tricky, especially in the face of code that isn't "clean" with its use of exceptions. – Craig Mar 08 '22 at 20:02
  • Unfortunately, the newer versions of VS prevent the use of `On Error` with Edit and Continue. :( and I am just now forced off using VS2013 for my last two projects the really benefited from it. Yeah, that is my basic strategy as well for production code - one top level try..catch and only sporadic, specific situational ones. My years with Javascript have taught me to extensively use defensive measures rather than reactionary error handling in my public facing code and has taught me to be less lazy as a programmer... – Brad Mathews Mar 08 '22 at 21:23