1

I have a requirement to connect to the server and collect data for processing.

Here is the Connect method

private readonly ServerComLibrary _vMyServer;// this is initialised in constructor 

public ConnectToServer(string servername)
{
  _vMyServer.connectToServerByName("ssl",servername);

}

_vMyServer has below events

  • onConnectSucceeded - I will collect data and copy it to excel file
  • onConnectFailed - just log the exception

Here is a connection success event

private void Handle_OnConnectSucceeded()
{
     //collect data and create excel 
}

Here is a Failed event

private void Handle_OnConnectFailed(int hr)
{
     //log exception
}

Everything works fine!

But, now my requirement is to connect to multiple servers one by one

List<Server> servers = ConfigurationManager.GetSection("servers") as List<Server>;
var datacollectionTasks = new List<Task>();
foreach (var server in servers)
{
   var data = Task.Run(() => ConnectToServer(server.serveraddress));
   datacollectionTasks.Add(dataFix);
}
await Task.WhenAll(datacollectionTasks);

I want to start the second task only after first task of connecting to the server and creating excel generation compleats or connection fails.

How can I do it? I may use ContinueWith but not sure how to confirm if events fired and job completed.

kudlatiger
  • 3,028
  • 8
  • 48
  • 98

1 Answers1

3

my requirement is to connect to multiple servers one by one

Then I'm not sure why you're using Task.WhenAll.

How can I do it? I may use ContinueWith but not sure how to confirm if events fired and job completed.

In order to chain tasks, you need a task to chain onto. Specifically, change the events into a Task by using TaskCompletionSource<T>:

public static class ServerComLibraryExtensions
{
  public static Task ConnectAsync(this ServerComLibrary @this, string protocol, string host)
  {
    var tcs = new TaskCompletionSource<object>();
    Action onSucceeded = null;
    Action<int> onFailed = null;
    onSuccess = () =>
    {
      @this.OnConnectSucceeded -= onSucceeded;
      @this.OnConnectFailed -= onFailed;
      tcs.TrySetResult(null);
    };
    onFailed = hr =>
    {
      @this.OnConnectSucceeded -= onSucceeded;
      @this.OnConnectFailed -= onFailed;
      tcs.TrySetException(Marshal.GetExceptionForHR(hr));
    };
    @this.OnConnectSucceeded += onSucceeded;
    @this.OnConnectFailed += onFailed;
    @this.connectToServerByName(protocol, host);
    return tcs.Task;
  }
}

Now that the connect operation is represented as a Task instead of events, it can naturally be "chained" by using the await keyword:

List<Server> servers = ConfigurationManager.GetSection("servers") as List<Server>;
foreach (var server in servers)
{
  await _vMyServer.ConnectAsync("ssl", server.serveraddress);
  // collect data and create excel
}
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • so you mean, if OnConnectSucceeded event takes 5 mins to succeed, or timed out, the code will not pick the next item from foreach loop? then it's great. – kudlatiger Jul 18 '19 at 09:50
  • Second question: How do I pass dependencies to extension classes? I want to log details using logger during connection success and failure events. This may not be possible while using extension methods or I believe we should try to avoid extension methods unless they only work on internal data. – kudlatiger Jul 18 '19 at 10:01
  • 1
    @kudlatiger: 1) Yes. 2) That sounds like a completely different question; you'll get better results asking a separate question on SO rather than asking in a comment on an answer. – Stephen Cleary Jul 18 '19 at 13:40
  • Thanks for input . I have created a question. https://stackoverflow.com/questions/57107015/how-do-i-pass-dependent-classes-to-extension-methods – kudlatiger Jul 19 '19 at 06:51
  • COM reference and static classes might cause memory issue i believe – kudlatiger Jul 19 '19 at 08:57