In VB.Net, how can I create a method that waits for a variable number of asynchronous calls to complete, and then returns a result? - vb.net

How can I code a method in VB.Net 2012 that waits for a variable number of asynchronous calls to complete, and only when all calls finish will then return a result?
I'm writing an app that retrieves a value from various web pages, and then returns the sum of those values. The number of values to retrieve will be determined by the user at runtime. As web retrieval is asynchronous by nature, I'm trying to make the app more efficient by coding it as such. I've just read about the keywords Async and Await, which seem perfect for the job. I also found this example of how to do it in C#: Run two async tasks in parallel and collect results in .NET 4.5.
But there are two issues with this example: 1) At first glance, I don't know how to make the same thing happen in VB.Net, and 2) I don't know how it could be redesigned to handle a variable number of called tasks.
Here's a pseudo-translation from the example, of what I hope to achieve:
Function GetSumOfValues(n as Integer)
For i = 1 To n
GetValueAsync<i>.Start()
Next i
Dim result = Await Task.WhenAll(GetValueAsync<?*>)
Return result.Sum()
End Function
Note the question mark, as I'm not sure if it's possible to give WhenAll a "wildcarded" group of tasks. Perhaps with an object collection?

You can use this example of using tasks with Task.WaitAll
Now, to collect data asynchronously, you can use a static method with sync lock. Or one of the synchronized collections

Related

Rx Observable only works once?

I have the following code which is being called from a Web Api. As you can see I want to return as soon as I can and shift the work onto the threadpool. (The client polls to see when the job is complete. But the polling is nothing to do with this. The purpose of these routines is simply to extract data and write a file away in background whilst maintaining the progress of the job in a table. The client will interrogates this to determine whether the file is ready so I'm not trying to push progress messages to the client.)
Public Function Extract(filepath as string, ...) as ExtractResult
dim source = ExtractInternal(filepath, ...)
' works first time it is called only!
using source.SubscribeOn(ThreadPoolScheduler.Instance)
.SubScribe()
end using
' works every time it is called ...
dim subscription = source.SubscribeOn(ThreadPoolScheduler.Instance)
.SubScribe()
Return New ExtractResult()
End Function
Public Function ExtractInternal(filepath as string, ...) as IObservable(of Unit)
return Observable.Create(of Unit)
Function()
....
uses filepath here
Return Disposable.Empty
End Function
End Function
As you can see in my comments, if I use auto-disposing of Using ..., I am finding that the observable gets called on the first occasion but not thereafter. Whereas if I assign the subscription to a local var it works every time the web call invokes the routine but I'm concerned that I'm actually leaving stuff hanging around.
Could someone explain why the observable doesn't get re-instantiated on subsequent calls and perhaps explain how I can get it to work every time and tidy up afterwards properly.
EDIT:
So I ended up using Observable.Defer which seems tom give me what I am after ...
Public Function Extract(filepath as string, ...) As ExtractResult
Observable.Defer(Function() ExtractInternal(filepath, ...) _
.SubscribeOn(NewThreadScheduler.Default) _
.Subscribe()
Return New ExtractResult()
End Function
I'm wondering if this is perhaps the correct way to do it to give me proper disposal whilst also using the current parameter values.
Could anyone confirm or correct?
EDIT 2
That was wrong! In fact if I rewrite it as
Public Function Extract(filepath as string, ...) As ExtractResult
Using Observable.Defer(Function() ExtractInternal(filepath, ...)
.SubscribeOn(NewThreadScheduler.Default) _
.Subscribe()
End Using
Return New ExtractResult()
End Function
I get the same behaviour as I originally was getting when I wrote the post.
One thing (amongst many) I don't understand is why if the observable is local var, when a second call is made to the Extract method, another observable is not created and subscribed to? It seems to go against scoping logic if I am actually referencing the same observable under the hood? I've obviously misunderstood.
Many thx
S
Yes, when you dispose the subscription, it stops receiving notifications.
You should keep it in an instance field and have the class implement disposable. Consumers of this class can then dispose it at their convenience.
In your Dispose implementation, you call subscription.Dispose().

Why does defining a variable of type of Smo.Server cause a 10 second delay

Bear with me here, ok!!
We use SMO a lot to do all kinds of things, including to check for the presence of particular stored procedures in the database. So we have a method in our data access class called HasProc, which returns a boolean. It's in a part of the application that hasn't been changed for over a year, possibly two years.
Lately, it's been taking ages (10s) to return a value, and I've been trying to pin down why.
It turns out that even defining the variable that will hold the SMO Server (not instantiating it, just defining it) causes a 10s delay in the code arriving into the function.
Here's the relevant bit of the code, which just returns True now, for clarity:
Public Function HasProc(ByVal storedProcName As String) As Boolean
Dim s As Microsoft.SqlServer.Management.Smo.Server
Return True
End Function
In Visual Studio 12, stepping through the code using F11, the 10 second delay happens before the code highlight arrives at Public Function etc...
If I comment out the Dim statement, it all runs instantly.
Even more weirdly, if I disable my ethernet adaptor, the delay does not occur.
This is reproducible across three computers. I'm using VS2012, and SMO v11, to which we recently upgraded in order to support SQL Server 2012.
The other thing is that the delay happens even if the Return True statement is before, rather than after the Dim statement.
Any ideas?
This would happen if the static initializer for that class performs network IO (which is generally a bad idea).
If you pause the debugger during the delay, you can find out exactly what it's doing.

Void-returning functions in UML sequence diagrams

I have a problem with the sequence model seen in the diagram below, specifically where the System object is creating a new Number. In this case, there is no need for a return message since the function SaveInput(n), both in System and Number, is the end of the line for that portion of the program, but unless I include one, the modeller reshaped my diagram into the other one I've uploaded here, and I can't see how to arrange the messages so that my program will work the way I intend without including the return message (the one without a name) from Number to System, since the functions SaveInput() both return a void.
How should void-returning functions be handled in sequence diagrams so that they behave correctly? I have opened the message properties and explicitly defined it as returning a void, but that hasn't helped.
When A calls operation b in B, the "return" arrow from B to A indicates the end of the operation b has finished its execution. This doesn´t mean that as part of the return message you have to return a value, it only means that the execution is done and you can continue with the next messages. Visually, most tools also use these return messages to manage the life bar of the object.

IEnumerable yield return combined with .AsParallel()

I've written some code to try and describe my concern:
static void Main(string[] args)
{
IEnumerable<decimal> marks = GetClassMarks();
IEnumerable<Person> students = GetStudents();
students.AsParallel().ForAll(p => GenerateClassReport(p, marks));
Console.ReadKey();
}
GetClassMarks uses yield return in it from my weird data source. Assume that GenerateClassReport does basically a marks.Sum()/marks.Count() to get the class average.
From what I understand, students.AsParallel().ForAll is a parallel foreach.
My worry is what is going to happen inside the GetClassMarks method.
Is it going to be enumerated once or many times?
What order is the enumeration going to happen in?
Do I need to do a .ToList() on marks to make sure it is only hit once?
Is it going to be enumerated once or many times?
Assuming that GenerateClassReport() enumerates marks once, then marks will be enumerated once for each element in students.
What order is the enumeration going to happen in?
Each thread will enumerate the collection in its default order, but several threads will do so concurrently. The concurrent enumeration order is generally unpredictable. Also, you should note that the number of threads is limited and variable, so most likely not all of the enumerations will occur concurrently.
Do I need to do a .ToList() on marks to make sure it is only hit once?
If GetClassMarks() is an iterator (i.e. it uses the yield construct), then its execution will be deferred and it will be called once for each time marks is enumerated (i.e. once for each element in students). If you use IEnumerable<decimal> marks = GetClassMarks().ToList() or if GetClassMarks() internally returns a concrete list or array, then GetClassMarks() will be executed immediately and the results will be stored and enumerated in each of the parallel threads without calling GetClassMarks() again.
If GetClassMarks is an iterator -- that is, if it uses yield internally -- then it is effectively a query that will be re-executed whenever you call marks.Sum(), marks.Count() etc.
It's almost impossible to predict the order of execution in a parallel query.
Yes. The following will ensure that GetClassMarks is only executed once. Subsequent calls to marks.Sum(), marks.Count() etc will use the concrete list rather than re-executing the GetClassMarks query.
List<decimal> marks = GetClassMarks().ToList();
Note that points 1 and 3 apply whether or not you're using AsParallel. The GetClassMarks query will be executed exactly the same number of times in either case (assuming that the rest of the code, except for the parallel aspects, is the same).
Is it going to be enumerated once or many times?
Just once.
What order is the enumeration going to happen in?
The iterator (function using yield) determines the order.
Do I need to do a .ToList() on marks to make sure it is only hit once?
No.
AsParallel only iterates through its input once, partitioning the input into blocks which are dispatched to worker threads.

RFC for remote call transaction

How do I call the SAP report (for example RSPARAM) with help JCo?
What RFC may be used to remotely call SA38 transaction with RSPARAM (e.t.c.) as parameter and then return results for later work ?
RFC is for calling function modules, not programs. It's possible to use some generic function module to start a report, but since you'll usually want to process the results of the program and the program does not know that it was meant to deliver its results in a machine-readable way, you probably won't get too far this was. What exactly are you trying to do?
With the nearly infinite possible results of calling a transaction, i don't think there is a RFC to execute such an operation and return a result. What would be the result in case of an ALV display, or if the program then wait for some interactions ?
You can display a transaction in SAP portal using transactions Iviews. You're then using the portal page as a HTMLGui for your transaction.
also, some FM can sometime be used to perform operations instead of a full program (ie HR_INFOTYPE_OPERATION instead of pa30).
regards
Guillaume
Edition : since you want the result of RRSPARAM, you could encapsulate the "important" part (form SHOW_ACTUAL_PAR_VALUES_ALV) in a module function accessible by RFC, and returning a table of CST_RSPFPAR_ALV (ie the same structure that is displayed in the report)
regards
If You don't find a function to call, just create one by yourself. Tag it as callable from outside via RFC and in the coding, perform such things as "submit report xyz with param1 = value1 ... and return ... exporting list to memory". Then you can even return list output from this. Define the interface of the freshly created function module as you need (that means, report name as input, list output as a table of strings, e.g.). Attention, there is of course a big security risk, having an remote function accepting variable reportnames. But I am sure You know :-)