In our application we use async calls. These calls we need to wait for so we use await. But we notice that the application continues the application some where else on a await from the HttpClient.SendAsync. We reproduced it with the following code:-
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace AsyncExperiment
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("1");
var adapter = new Adapter();
Console.WriteLine("2");
var result = Task.Factory.StartNew(() => adapter.Start()).Result;
Console.WriteLine("21");
Console.ReadKey();
}
}
public class Adapter
{
public async Task<string> Start()
{
Console.WriteLine("3");
return await CollectionAccessor.ExecuteWithinScope(async collection => {
Console.WriteLine("8");
var adapter = new AsyncSearchAdapter();
Console.WriteLine("9");
var result = await adapter.GetSearchAsync();
Console.WriteLine("19");
Console.WriteLine(result);
Console.WriteLine("20");
return "";
});
}
}
public class Client
{
public async Task<string> Get()
{
Console.WriteLine("12");
var requestMessage = new HttpRequestMessage(HttpMethod.Get, "https://22ad5e1e-688d-4ba4-9287-6bb4a351fd05.mock.pstmn.io/test");
Console.WriteLine("13");
HttpClient httpClient = new HttpClient();
Console.WriteLine("14");
HttpResponseMessage response = await httpClient.SendAsync(requestMessage);
Console.WriteLine("15");
if(response.IsSuccessStatusCode){
Console.WriteLine("16a");
return await response.Content.ReadAsStringAsync();
}
Console.WriteLine("16b");
return null;
}
}
public class AsyncSearchAdapter
{
public async Task<string> GetSearchAsync()
{
Console.WriteLine("10");
var client = new Client();
Console.WriteLine("11");
var response = await client.Get();
Console.WriteLine("17");
if(response.Equals("{'test', 'test'}")){
Console.WriteLine("18a");
return response;
}
Console.WriteLine("18b");
return response;
}
}
public static class CollectionAccessor
{
public static TReturn ExecuteWithinScope<TReturn>(Func<ICatalogCollection, TReturn> func)
{
Console.WriteLine("4");
if(func == null) throw new ArgumentNullException("func");
Console.WriteLine("5");
using(var catalogCollection = Resolver())
{
Console.WriteLine("7");
return func(catalogCollection);
}
}
public static ICatalogCollection Resolver()
{
Console.WriteLine("6");
return new CatalogCollection();
}
}
public interface ICatalogCollection: IDisposable
{
string notImportant { get;}
}
public class CatalogCollection : ICatalogCollection, IDisposable
{
public string notImportant { get;}
public CatalogCollection(){
notImportant = "test";
}
public void Dispose()
{
throw new NotImplementedException();
}
}
}
We expect the order of the logs to be
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21
but we get the order like this:
1,2,3,4,5,6,7,8,9,10,11,12,13,14,21,15,16,17,18,19,20
Could someone explain to me why this is happening in this order. And how to get it in the expected order?
Thanks!!!
You are running async function (adapter.Start()) and not waiting for it. Try to change
var result = Task.Factory.StartNew(() => adapter.Start()).Result;
to
var result = adapter.Start().Result;
or
var result = Task.Factory.StartNew(() => adapter.Start().Result).Result;
and I guess you are doing same problem here
await CollectionAccessor.ExecuteWithinScope(async collection => {...})
just ensure that CollectionAccessor.ExecuteWithinScope will handle awaiting passed function into it. Like
async Task CollectionAccessor.ExecuteWithinScope(Func <ICollection, Task> action)
{
...
await (action(collection));
...
}
or at least returning it
async Task CollectionAccessor.ExecuteWithinScope(Func <ICollection, Task> action)
{
...
return (action(collection));
}
UPD
Right here
public static TReturn ExecuteWithinScope<TReturn>(Func<ICatalogCollection, TReturn> func)
{
Console.WriteLine("4");
if (func == null) throw new ArgumentNullException("func");
Console.WriteLine("5");
using (var catalogCollection = Resolver())
{
Console.WriteLine("7");
return func(catalogCollection); // <<<<<<<HERE
}
}
you are creating Task which is not finished yet and you returning it and disposing collection before task fininshed.
I guess you need to wait task for complete and only after it return it. Like
public static async Task<TReturn> ExecuteWithinScope<TReturn>(Func<ICatalogCollection, Task<TReturn>> func)
{
Console.WriteLine("4");
if (func == null) throw new ArgumentNullException("func");
Console.WriteLine("5");
using (var catalogCollection = Resolver())
{
Console.WriteLine("7");
return await func(catalogCollection); // waiting task for completition
}
}
OR you need to dispose collection inside task, like
public static TReturn ExecuteWithinScope<TReturn>(Func<ICatalogCollection, TReturn> func)
{
Console.WriteLine("4");
if (func == null) throw new ArgumentNullException("func");
Console.WriteLine("5");
//using (var catalogCollection = Resolver()) // not in using!
{
var catalogCollection = Resolver();
Console.WriteLine("7");
return func(catalogCollection);
}
}
And then
return await CollectionAccessor.ExecuteWithinScope(async collection =>
{
Console.WriteLine("8");
var adapter = new AsyncSearchAdapter();
Console.WriteLine("9");
var result = await adapter.GetSearchAsync();
Console.WriteLine("19");
Console.WriteLine(result);
Console.WriteLine("20");
collection.Dispose(); //// Disposing!
return "";
});
From my point first approach (await func(catalogCollection);) - is best one
Related
Hello I have the following problem:
I want to perform something similar to a transaction. I want to execute a number of async operations after I receive an external trigger.Therefore I am using a TaskCompletionSource that gets set in a method representing the trigger :TriggerTransaction.This trigger method gets called in Main on the thread pool when i press a specific console key.
After I press the A keyword the TriggerTransaction gets executed and the TaskCompletionSource-s get set.Still the main thread does not compute the sum of the two awaited tasks.
class Program
{
public static Task<Task<int>> TransactionOperation1()
{
TaskCompletionSource<Task<int>> tcs = new TaskCompletionSource<Task<int>>();
tasks.Add(tcs);
Task<Task<int>> result = tcs.Task;
return result;
}
public static Task<Task<int>> TransactionOperation2()
{
TaskCompletionSource<Task<int>> tcs = new TaskCompletionSource<Task<int>>();
tasks.Add(tcs);
Task<Task<int>> result = tcs.Task;
return result;
}
public static async Task<int> ExecuteTransactionOnDB()
{
await Task.Delay(1000);
return 5;
}
public static async Task TriggerTransaction()
{
int value = await ExecuteTransactionOnDB();
foreach (var item in tasks)
{
item.SetResult(value);
}
}
public static List<dynamic> tasks = new List<dynamic>();
static async Task Main(string[] args)
{
Task<Task<int>> a = TransactionOperation1();
Task<Task<int>> b = TransactionOperation2();
Task.Run(async() =>
{
while (Console.ReadKey().Key != ConsoleKey.A) ;
await TriggerTransaction();
});
if (!File.Exists("D:\\data.txt"))
{
File.Create("D:\\data.txt");
}
using(FileStream stream=new FileStream("data.txt",FileMode.Append,FileAccess.Write))
{
int sum=await await a + await await b;//thread wont pass this line when tasks are set.
ReadOnlyMemory<byte> bytes = Encoding.UTF8.GetBytes(sum);
stream.Write(bytes.ToArray());
}
Console.WriteLine(await await a + await await b);
}
}
}
P.S If you are wondering why I did use a List<dynamic> to store the TaskCompletionSource-s ,it's because the TransactionOperations will differ in return type.Some of them will return int,others String ..Bool..etc.
For a better understanding i made a schema-
As you will see there are:
-A list where i want to store the TCS-es
-Some Calls that are completed only after the external trigger was set(the transaction was executed)
As you can see in the Calls,all have different return types.
Why would you need a Task<Task<int>>? Simply Task<int> is enough, and accordingly, TaskCompletionSource<int>. And you also get rid of an awkward await await ..., which isn't required in your case either.
Note that I also added Close() to the stream returned by File.Create().
Here is a working version of the program:
class Program
{
public static Task<int> TransactionOperation1()
{
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
tasks.Add(tcs);
return tcs.Task;
}
public static Task<int> TransactionOperation2()
{
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
tasks.Add(tcs);
return tcs.Task;
}
public static async Task<int> ExecuteTransactionOnDB()
{
await Task.Delay(1000);
return 5;
}
public static async Task TriggerTransaction()
{
int value = await ExecuteTransactionOnDB();
foreach (var item in tasks)
{
item.SetResult(value);
}
}
public static List<dynamic> tasks = new List<dynamic>();
static async Task Main(string[] args)
{
Task<int> a = TransactionOperation1();
Task<int> b = TransactionOperation2();
Task input = Task.Run(async () => {
while (Console.ReadKey().Key != ConsoleKey.A);
await TriggerTransaction();
});
if (!File.Exists("C:\\temp\\data.txt"))
{
File.Create("C:\\temp\\data.txt").Close();
}
using (FileStream stream = new FileStream("C:\\temp\\data.txt", FileMode.Append, FileAccess.Write))
{
int sum = await a + await b; // now it works ok
var bytes = Encoding.UTF8.GetBytes(sum.ToString());
stream.Write(bytes);
}
Console.WriteLine(await a + await b);
}
}
Check out the modified version of the code, it produce the expected result, by executing the Task created using TaskCompletionSource. I have made the code the Generic too, so that you don't need to use the dynamic type and define the datatype at the compile time
static async Task Main(string[] args)
{
var a = Program<int>.TransactionOperation1();
var b = Program<int>.TransactionOperation2();
await Task.Run(async() =>
{
Console.ReadLine();
await Program<int>.TriggerTransaction(5);
});
if (!File.Exists("D:\\data.txt"))
{
File.Create("D:\\data.txt");
}
using (FileStream stream = new FileStream("D:\\data.txt", FileMode.Append, FileAccess.Write))
{
int sum = await a + await b;//thread wont pass this line when tasks are set.
var bytes = Encoding.UTF8.GetBytes(sum.ToString());
stream.Write(bytes, 0, bytes.Length);
}
Console.WriteLine(await a + await b);
}
class Program<T>
{
public static Task<T> TransactionOperation1()
{
var tcs = new TaskCompletionSource<T>();
tasks.Add(tcs);
return tcs.Task;
}
public static Task<T> TransactionOperation2()
{
var tcs = new TaskCompletionSource<T>();
tasks.Add(tcs);
return tcs.Task;
}
public static async Task<T> ExecuteTransactionOnDB(T t)
{
return await Task.FromResult(t);
}
public static async Task TriggerTransaction(T t)
{
T value = await ExecuteTransactionOnDB(t);
foreach (var item in tasks)
{
item.SetResult(value);
}
}
public static List<TaskCompletionSource<T>> tasks = new List<TaskCompletionSource<T>>();
}
Following are the important modifications:
List<dynamic> is replaced by List<TaskCompletionSource<T>>
TransactionOperation1/2 have return type Task<T>, which is the Task created using the TaskCompletionSource<T>
Added an extra await to the Task.Run, which executes the TriggerTransaction internally, though you can replace the following code:
await Task.Run(async() =>
{
Console.ReadLine();
await Program<int>.TriggerTransaction(5);
});
with
await Program<int>.TriggerTransaction(5);
Now it produces the result as you expect, it will sum up the two integers. Few more small changes like removing Task.Delay, which is not required
EDIT 1 - Using Task.WhenAll
static async Task Main(string[] args)
{
var a = Program.TransactionOperation1(5);
var b = Program.TransactionOperation1(5);
Console.ReadLine();
var taskResults = await Task.WhenAll(a,b);
dynamic finalResult = 0;
foreach(var t in taskResults)
finalResult += t;
if (!File.Exists("D:\\data.txt"))
{
File.Create("D:\\data.txt");
}
using (FileStream stream = new FileStream("D:\\data.txt", FileMode.Append, FileAccess.Write))
{
var bytes = Encoding.UTF8.GetBytes(finalResult.ToString());
stream.Write(bytes, 0, bytes.Length);
}
Console.WriteLine(finalResult);
}
class Program
{
public static Task<dynamic> TransactionOperation1(dynamic val)
{
return Task<dynamic>.Run(() => val);
}
public static Task<dynamic> TransactionOperation2(dynamic val)
{
return Task<dynamic>.Run(() => val);
}
}
I am using NDepend and in the following code, it detects this code smell.
But if I add readonly, then it wont compile.
namespace todo
{
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
public static class DocumentDBRepository<T> where T : class
{
private static readonly string DatabaseId = ConfigurationManager.AppSettings["database"];
private static readonly string CollectionId = ConfigurationManager.AppSettings["collection"];
private static DocumentClient client;
public static async Task<T> GetItemAsync(string id)
{
try
{
Document document = await client.ReadDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id));
return (T)(dynamic)document;
}
catch (DocumentClientException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
return null;
}
else
{
throw;
}
}
}
public static async Task<IEnumerable<T>> GetItemsAsync(Expression<Func<T, bool>> predicate)
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = -1 })
.Where(predicate)
.AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
public static async Task<Document> CreateItemAsync(T item)
{
return await client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId), item);
}
public static async Task<Document> UpdateItemAsync(string id, T item)
{
return await client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), item);
}
public static async Task DeleteItemAsync(string id)
{
await client.DeleteDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id));
}
public static void Initialize()
{
client = new DocumentClient(new Uri(ConfigurationManager.AppSettings["endpoint"]), ConfigurationManager.AppSettings["authKey"]);
CreateDatabaseIfNotExistsAsync().Wait();
CreateCollectionIfNotExistsAsync().Wait();
}
private static async Task CreateDatabaseIfNotExistsAsync()
{
try
{
await client.ReadDatabaseAsync(UriFactory.CreateDatabaseUri(DatabaseId));
}
catch (DocumentClientException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
await client.CreateDatabaseAsync(new Database { Id = DatabaseId });
}
else
{
throw;
}
}
}
private static async Task CreateCollectionIfNotExistsAsync()
{
try
{
await client.ReadDocumentCollectionAsync(UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId));
}
catch (DocumentClientException e)
{
if (e.StatusCode == System.Net.HttpStatusCode.NotFound)
{
await client.CreateDocumentCollectionAsync(
UriFactory.CreateDatabaseUri(DatabaseId),
new DocumentCollection { Id = CollectionId },
new RequestOptions { OfferThroughput = 1000 });
}
else
{
throw;
}
}
}
}
}
The warning line is:
private static DocumentClient client;
How would you recommend to fix this NDepend warning?
Rule Description:
This rule warns about static fields that are not declared as read-only.
In Object-Oriented-Programming the natural artifact to hold states that can be modified is instance fields. Such mutable static fields create confusion about the expected state at runtime and impairs the code testability since the same mutable state is re-used for each test.
More discussion on the topic can be found here: http://codebetter.com/patricksmacchia/2011/05/04/back-to-basics-usage-of-static-members/
Try moving the initialization to the declaration:
private static readonly DocumentClient client = new DocumentClient( . . . . );
I want to make a Wifimanager class for UWP (Raspberry pi 3)
I looked at some tutorials and figured i was good to go.
So I came up with this:
public class WifiManager
{
private WiFiAdapter adapter;
public List<WifiNetwork> Networks;
public WifiManager()
{
Initialize();
}
private async void Initialize()
{
var access = await WiFiAdapter.RequestAccessAsync();
if (access != WiFiAccessStatus.Allowed)
{
throw new WifiAdaperAccessDeniedException();
}
else
{
var result = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
if (result.Count >= 1)
{
adapter = await WiFiAdapter.FromIdAsync(result[0].Id);
}
else
{
throw new NoWifiAdapterFoundException();
}
}
}
public async Task GetAvailableNetWorksAsync()
{
try
{
if (adapter != null)
{
await adapter.ScanAsync();
}
}
catch (Exception err)
{
throw new WifiAdaperAccessDeniedException();
}
Networks = new List<WifiNetwork>();
foreach(var network in adapter.NetworkReport.AvailableNetworks)
{
Networks.Add(new WifiNetwork(network, adapter));
}
}
}
However when I try to get the Available Networks using the async function the adapter is null. When I remove the if statement around await adapter.ScanAsync(); I get an AccessViolation.
I dont have much experience with async tasks and such, but the tutorials i found did not gave me the explaination i needed to fix this.
The problem is that you are calling an async method from the constructor. Since you do no wait for the result and are probably calling GetAvailableNetWorksAsync directly after the constructor the adapter variable has not been set yet..
You need to take it out of the constructor like this:
public class WifiManager
{
private WiFiAdapter adapter;
public List<WifiNetwork> Networks;
public async Task InitializeAsync()
{
var access = await WiFiAdapter.RequestAccessAsync();
if (access != WiFiAccessStatus.Allowed)
{
throw new WifiAdaperAccessDeniedException();
}
else
{
var result = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
if (result.Count >= 1)
{
adapter = await WiFiAdapter.FromIdAsync(result[0].Id);
}
else
{
throw new NoWifiAdapterFoundException();
}
}
}
public async Task GetAvailableNetWorksAsync()
{
try
{
if (adapter != null)
{
await adapter.ScanAsync();
}
}
catch (Exception err)
{
throw new WifiAdaperAccessDeniedException();
}
Networks = new List<WifiNetwork>();
foreach(var network in adapter.NetworkReport.AvailableNetworks)
{
Networks.Add(new WifiNetwork(network, adapter));
}
}
}
Now your calling code probably looks like this:
var wifiManager = new WifiManager();
await wifiManager.GetAvailableNetWorksAsync();
change that to
var wifiManager = new WifiManager();
await wifiManager.InitializeAsync();
await wifiManager.GetAvailableNetWorksAsync();
Take away: do not call async methods in a constructor since you cannot await completion using the await keyword and using .Wait() or .Result might give other troubles . Applies for 99% of all situations.
Another approach could be something like:
public class WifiManager
{
private WiFiAdapter adapter;
public List<WifiNetwork> Networks;
pivate async Task InitializeAsync()
{
var access = await WiFiAdapter.RequestAccessAsync();
if (access != WiFiAccessStatus.Allowed)
{
throw new WifiAdaperAccessDeniedException();
}
else
{
var result = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
if (result.Count >= 1)
{
adapter = await WiFiAdapter.FromIdAsync(result[0].Id);
}
else
{
throw new NoWifiAdapterFoundException();
}
}
}
public async Task GetAvailableNetWorksAsync()
{
try
{
if (adapter == null)
{
await InitializeAsync();
}
if (adapter != null)
{
await adapter.ScanAsync();
}
}
catch (Exception err)
{
throw new WifiAdaperAccessDeniedException();
}
Networks = new List<WifiNetwork>();
foreach(var network in adapter.NetworkReport.AvailableNetworks)
{
Networks.Add(new WifiNetwork(network, adapter));
}
}
}
If I start with a full console app:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace ConsoleApplication3
{
internal class Program
{
private static void Main(string[] args)
{
var tas = new List<TaskAuditor<string>>();
// I want to await these when they actually run so another thread can use it while
// wait for (example) my EF/SQL to run Async
tas.Add(new TaskAuditor<string>(await GetName(string.Empty, 1)));
tas.Add(new TaskAuditor<string>(await GetName(string.Empty, 2)));
var running = new Task<String>[2];
foreach (var ta in tas)
{
running[0] = ta.Start();
}
var runningCount = tas.Count;
while (runningCount > 0)
{
var idx = Task.WaitAny(running);
runningCount--;
var task = running[idx];
var ta = tas.FirstOrDefault(t => t.Task == task);
Console.WriteLine(ta.Duration.ToString());
}
foreach (var ta in tas)
{
Console.WriteLine(ta.Task.Result);
}
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
public async Task<string> GetName(string constr, int id)
{
string result = id.ToString();
// EF/SQL Async goes here
if (string.IsNullOrWhiteSpace(constr))
{
await Task.Delay(1000 * id);
}
return result;
}
}
public class TaskAuditor<T>
{
private Task<T> _task;
private Stopwatch _sw = new Stopwatch();
public TaskAuditor(Task<T> task)
{
_task = task;
}
public Task<T> Start()
{
_sw.Start();
_task.Start();
_sw.Stop();
return _task;
}
public TimeSpan? Duration()
{
TimeSpan? result = null;
if (!_sw.IsRunning)
{
result = _sw.Elapsed;
}
return result;
}
public Task<T> Task
{
get
{
return _task;
}
}
}
}
DotNetFiddle Sample.
The problem is that I need to await the method and it turns into an Async nightmare I can't quite figure out.
First, your auditor needs some work. As I describe on my blog, you can't call Start on a Promise Task. If you want to represent an operation that results in a task, then use Func<Task<T>>. The next problem is that you're stopping the stopwatch before the task actually completes:
public class TaskAuditor<T>
{
private Func<Task<T>> _func;
private Stopwatch _sw = new Stopwatch();
public TaskAuditor(Func<Task<T>> func)
{
_func = func;
}
public async Task<T> StartAsync()
{
_sw.Start();
try
{
return await _func();
}
finally
{
_sw.Stop();
}
}
public TimeSpan? Duration()
{
TimeSpan? result = null;
if (!_sw.IsRunning)
{
result = _sw.Elapsed;
}
return result;
}
}
Next, your calling method should use modern conveniences like Task.WhenAll, and a separate method to handle the result as they each complete instead of trying to pull the results out of a list:
private static async Task<string> ProcessAsync(TaskAuditor<string> auditor)
{
try
{
return await auditor.StartAsync();
}
finally
{
Console.WriteLine(auditor.Duration().ToString());
}
}
private static async Task MainAsync()
{
var tas = new List<TaskAuditor<string>>();
tas.Add(new TaskAuditor<string>(() => GetName(string.Empty, 1)));
tas.Add(new TaskAuditor<string>(() => GetName(string.Empty, 2)));
var running = tas.Select(ta => ProcessAsync(ta));
var results = await Task.WhenAll(running);
foreach (var result in results)
{
Console.WriteLine(result);
}
}
This also keeps the code asynchronous up to the point where it can't be asynchronous, namely, Main:
private static void Main()
{
MainAsync().Wait();
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
I have a method, shown below, which calls a service.
How can I run this method through thread?
public List<AccessDetails> GetAccessListOfMirror(string mirrorId,string server)
{
List<AccessDetails> accessOfMirror = new List<AccessDetails>();
string loginUserId = SessionManager.Session.Current.LoggedInUserName;
string userPassword = SessionManager.Session.Current.Password;
using (Service1Client client = new Service1Client())
{
client.Open();
accessOfMirror = client.GetMirrorList1(mirrorId, server, null);
}
return accessOfMirror;
}
In C# 3.5 or 4.0 you can do this.
var task = Task.Factory.StartNew<List<AccessDetails>>(() => GetAccessListOfMirror(mirrorId,server))
.ContinueWith(tsk => ProcessResult(tsk));
private void ProcessResult(Task task)
{
var result = task.Result;
}
In C# 4.5 there's the await/async keywords which is some sugar for above
public async Task<List<AccessDetails>> GetAccessListOfMirror(string mirrorId,string server)
var myResult = await GetAccessListOfMirror(mirrorId, server)
Try something like this:
public async Task<List<AccessDetails>> GetAccessListOfMirror(string mirrorId, string server)
{
List<AccessDetails> accessOfMirror = new List<AccessDetails>();
string loginUserId = SessionManager.Session.Current.LoggedInUserName;
string userPassword = SessionManager.Session.Current.Password;
using (Service1Client client = new Service1Client())
{
client.Open();
Task<List<AccessDetails>> Detail = client.GetMirrorList1(mirrorId, server, null);
accessOfMirror = await Detail;
}
return accessOfMirror;
}
Below is a helper class I use, it references RX.NET.
If you include that in your project, then you can thread stuff very simply - the code above you could spin off to a separate thread as follows:
int mirrorId = 0;
string server = "xxx";
ASync.Run<List<AccessDetails>>(GetAccessListOfMirror(mirrorId,server), resultList => {
foreach(var accessDetail in resultList)
{
// do stuff with result
}
}, error => { // if error occured on other thread, handle exception here });
Worth noting: that lambda expression is merged back to the original calling thread - which is very handy if you're initiating your async operations from a GUI thread for example.
It also has another very handy method: Fork lets you spin off multiple worker threads and causes the calling thread to block until all the sub-threads are either complete or errored.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Concurrency;
namespace MyProject
{
public static class ASync
{
public static void ThrowAway(Action todo)
{
ThrowAway(todo, null);
}
public static void ThrowAway(Action todo, Action<Exception> onException)
{
if (todo == null)
return;
Run<bool>(() =>
{
todo();
return true;
}, null, onException);
}
public static bool Fork(Action<Exception> onError, params Action[] toDo)
{
bool errors = false;
var fork = Observable.ForkJoin(toDo.Select(t => Observable.Start(t).Materialize()));
foreach (var x in fork.First())
if (x.Kind == NotificationKind.OnError)
{
if(onError != null)
onError(x.Exception);
errors = true;
}
return !errors;
}
public static bool Fork<T>(Action<Exception> onError, IEnumerable<T> args, Action<T> perArg)
{
bool errors = false;
var fork = Observable.ForkJoin(args.Select(arg => Observable.Start(() => { perArg(arg); }).Materialize()));
foreach (var x in fork.First())
if (x.Kind == NotificationKind.OnError)
{
if (onError != null)
onError(x.Exception);
errors = true;
}
return !errors;
}
public static void Run<TResult>(Func<TResult> todo, Action<TResult> continuation, Action<Exception> onException)
{
bool errored = false;
IDisposable subscription = null;
var toCall = Observable.ToAsync<TResult>(todo);
var observable =
Observable.CreateWithDisposable<TResult>(o => toCall().Subscribe(o)).ObserveOn(Scheduler.Dispatcher).Catch(
(Exception err) =>
{
errored = true;
if (onException != null)
onException(err);
return Observable.Never<TResult>();
}).Finally(
() =>
{
if (subscription != null)
subscription.Dispose();
});
subscription = observable.Subscribe((TResult result) =>
{
if (!errored && continuation != null)
{
try
{
continuation(result);
}
catch (Exception e)
{
if (onException != null)
onException(e);
}
}
});
}
}
}