Dynamically search with interrupts,via Tasks C# - c#

I'm working with Db(via SQLite.NET PCL,not async version). At current moment i have a listview with some data(taken from db),also i got a searchbar/entry(its nvm),where user can input some values and then,via LINQ i'm going to make a query and update SourceItems of my List.
So the problems comes with performance,because my DB got million records and simple LINQ query working very slow.In other words,when user enter too fast some data,application gets huge lags and sometimes can Crash.
To resolve this problem,some things come in my mind(theoretically solution):
1)Need to put method(which i make query into db) on Task(to unlock my main UI thread)
2)Initialize timer,then turn on and :
if 1 second passed,then => run my method(query) on Task(similiar to background thread)
if 1 second doesn't passed,then exit from anonymous method.
Something like that(similar) or any suggestions.Thanks!
UPD:
So to be honest,i tried too much and didnt get a good result
BTW,my current code(Snippets) :
1) My SearchMethod
public void QueryToDB(string filter)
{
this.BeginRefresh ();
if (string.IsNullOrWhiteSpace (filter))
{
this.ItemsSource = SourceData.Select(x => x.name); // Source data is my default List of items
}
else
{
var t = App.DB_Instance.FilterWords<Words>(filter); //FilterWords it's a method,where i make direct requests to the database
this.ItemsSource = t.Select(x => x.name);
}
this.EndRefresh ();
}
2)Searchbar.TextChanged (anonymous method)
searchBar.TextChanged +=async (sender, e) =>
{
ViewModel.isBusy = true; //also i got a indicator,to show progress,while query working
await Task.Run(()=> //my background,works fine
{
listview.QueryToDB(searchBar.Text);
});
ViewModel.isBusy = false; // after method is finished,indicator turn off
};
The main problem is how to implement this part(with these case's),where 1 second passed and only then i'm going to make query to update my sourceItems of list(everytime,when user inputs some value into searchbar,this trigger(timer) must refresh again to zero).
Any help will be appreciated,thanks!
PS Sorry for my eng. skills!

One way to do it is to combine async Task.Run and CancellationTokenSource:
CancellationTokenSource cancellationTokenSource;
searchView.TextChanged += async (sender, e) =>
{
if (cancellationTokenSource != null) cancellationTokenSource.Cancel();
cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
var searchBar = (sender as SearchBar);
if (searchBar != null)
{
string searchText = searchBar.Text;
try
{
await Task.Delay(650, cancellationToken);
if (cancellationToken.IsCancellationRequested) return;
var searchResults = await Task.Run(() =>
{
return ViewModel.Search(searchText);
});
if (cancellationToken.IsCancellationRequested) return;
ViewModel.YouItems.Repopulate(searchResults);
}
catch (OperationCanceledException)
{
// Expected
}
catch (Exception ex)
{
Logger.Error(ex);
}
}
};

You want to wait before actually performing your search. Killing a search task in midway can cause undefined behavior.
You want to save the current search filter and compared it again 1 second later. If that hasn't changed, do the search. Otherwise, abort :
searchBar.TextChanged += async (sender, e) =>
{
var filter = searchBar.Text;
await Task.Run(() =>
{
Thread.Sleep(1000);
if (filter == searchBar.Text)
listview.QueryToDB(searchBar.Text);
});
};
As to keep the view model updated, move your isBusy assignments inside QueryToDB because that is when your view model is truly busy :
public void QueryToDB(string filter)
{
this.BeginRefresh ();
ViewModel.isBusy = true;
// do your search
ViewModel.isBusy = false;
this.EndRefresh ();
}

Related

Parallel processing using TPL in windows service

I have a windows service which is consuming a messaging system to fetch messages. I have also created a callback mechanism with the help of Timer class which helps me to check the message after some fixed time to fetch and process. Previously, the service is processing the message one by one. But I want after the message arrives the processing mechanism to execute in parallel. So if the first message arrived it should go for processing on one task and even if the processing is not finished for the first message still after the interval time configured using the callback method (callback is working now) next message should be picked and processed on a different task.
Below is my code:
Task.Factory.StartNew(() =>
{
Subsriber<Message> subsriber = new Subsriber<Message>()
{
Interval = 1000
};
subsriber.Callback(Process, m => m != null);
});
public static void Process(Message message)
{
if (message != null)
{
// Processing logic
}
else
{
}
}
But using the Task Factory I am not able to control the number of tasks in parallel so in my case I want to configure the number of tasks on which messages will run on the availability of the tasks?
Update:
Updated my above code to add multiple tasks
Below is the code:
private static void Main()
{
try
{
int taskCount = 5;
Task.Factory.StartNewAsync(() =>
{
Subscriber<Message> consumer = new
Subcriber<Message>()
{
Interval = 1000
};
consumer.CallBack(Process, msg => msg!=
null);
}, taskCount);
Console.ReadLine();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
public static void StartNewAsync(this TaskFactory
target, Action action, int taskCount)
{
var tasks = new Task[taskCount];
for (int i = 0; i < taskCount; i++)
{
tasks[i] = target.StartNew(action);
}
}
public static void Process(Message message)
{
if (message != null)
{
}
else
{ }
}
}
I think what your looking for will result in quite a large sample. I'm trying just to demonstrate how you would do this with ActionBlock<T>. There's still a lot of unknowns so I left the sample as skeleton you can build off. In the sample the ActionBlock will handle and process in parallel all your messages as they're received from your messaging system
public class Processor
{
private readonly IMessagingSystem _messagingSystem;
private readonly ActionBlock<Message> _handler;
private bool _pollForMessages;
public Processor(IMessagingSystem messagingSystem)
{
_messagingSystem = messagingSystem;
_handler = new ActionBlock<Message>(msg => Process(msg), new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 5 //or any configured value
});
}
public async Task Start()
{
_pollForMessages = true;
while (_pollForMessages)
{
var msg = await _messagingSystem.ReceiveMessageAsync();
await _handler.SendAsync(msg);
}
}
public void Stop()
{
_pollForMessages = false;
}
private void Process(Message message)
{
//handle message
}
}
More Examples
And Ideas
Ok, sorry I'm short on time but here's the general idea/skeleton of what I was thinking as an alternative.
If I'm honest though I think the ActionBlock<T> is the better option as there's just so much done for you, with the only limit being that you can't dynamically scale the amount of work it will do it once, although I think the limit can be quite high. If you get into doing it this way you could have more control or just have a kind of dynamic amount of tasks running but you'll have to do a lot of things manually, e.g if you want to limit the amount of tasks running at a time, you'd have to implement a queueing system (something ActionBlock handles for you) and then maintain it. I guess it depends on how many messages you're receiving and how fast your process handles them.
You'll have to check it out and think of how it could apply to your direct use case as I think some of the details area a little sketchily implemented on my side around the concurrentbag idea.
So the idea behind what I've thrown together here is that you can start any number of tasks, or add to the tasks running or cancel tasks individually by using the collection.
The main thing I think is just making the method that the Callback runs fire off a thread that does the work, instead of subscribing within a separate thread.
I used Task.Factory.StartNew as you did, but stored the returned Task object in an object (TaskInfo) which also had it's CancellationTokenSource, it's Id (assigned externally) as properties, and then added that to a collection of TaskInfo which is a property on the class this is all a part of:
Updated - to avoid this being too confusing i've just updated the code that was here previously.
You'll have to update bits of it and fill in the blanks in places like with whatever you have for my HeartbeatController, and the few events that get called because they're beyond the scope of the question but the idea would be the same.
public class TaskContainer
{
private ConcurrentBag<TaskInfo> Tasks;
public TaskContainer(){
Tasks = new ConcurrentBag<TaskInfo>();
}
//entry point
//UPDATED
public void StartAndMonitor(int processorCount)
{
for (int i = 0; i <= processorCount; i++)
{
Processor task = new Processor(ProcessorId = i);
CreateProcessorTask(task);
}
this.IsRunning = true;
MonitorTasks();
}
private void CreateProcessorTask(Processor processor)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
Task taskInstance = Task.Factory.StartNew(
() => processor.Start(cancellationTokenSource.Token)
);
//bind status update event
processor.ProcessorStatusUpdated += ReportProcessorProcess;
Tasks.Add(new ProcessorInfo()
{
ProcessorId = processor.ProcessorId,
Task = taskInstance,
CancellationTokenSource = cancellationTokenSource
});
}
//this method gets called once but the HeartbeatController gets an action as a param that it then
//executes on a timer. I haven't included that but you get the idea
//This method also checks for tasks that have stopped and restarts them if the manifest call says they should be running.
//Will also start any new tasks included in the manifest and stop any that aren't included in the manifest.
internal void MonitorTasks()
{
HeartbeatController.Beat(() =>
{
HeartBeatHappened?.Invoke(this, null);
List<int> tasksToStart = new List<int>();
//this is an api call or whatever drives your config that says what tasks must be running.
var newManifest = this.GetManifest(Properties.Settings.Default.ResourceId);
//task Removed Check - If a Processor is removed from the task pool, cancel it if running and remove it from the Tasks List.
List<int> instanceIds = new List<int>();
newManifest.Processors.ForEach(x => instanceIds.Add(x.ProcessorId));
var removed = Tasks.Select(x => x.ProcessorId).ToList().Except(instanceIds).ToList();
if (removed.Count() > 0)
{
foreach (var extaskId in removed)
{
var task = Tasks.FirstOrDefault(x => x.ProcessorId == extaskId);
task.CancellationTokenSource?.Cancel();
}
}
foreach (var newtask in newManifest.Processors)
{
var oldtask = Tasks.FirstOrDefault(x => x.ProcessorId == newtask.ProcessorId);
//Existing task check
if (oldtask != null && oldtask.Task != null)
{
if (!oldtask.Task.IsCanceled && (oldtask.Task.IsCompleted || oldtask.Task.IsFaulted))
{
var ex = oldtask.Task.Exception;
tasksToStart.Add(oldtask.ProcessorId);
continue;
}
}
else //New task Check
tasksToStart.Add(newtask.ProcessorId);
}
foreach (var item in tasksToStart)
{
var taskToRemove = Tasks.FirstOrDefault(x => x.ProcessorId == item);
if (taskToRemove != null)
Tasks.Remove(taskToRemove);
var task = newManifest.Processors.FirstOrDefault(x => x.ProcessorId == item);
if (task != null)
{
CreateProcessorTask(task);
}
}
});
}
}
//UPDATED
public class Processor{
private int ProcessorId;
private Subsriber<Message> subsriber;
public Processor(int processorId) => ProcessorId = processorId;
public void Start(CancellationToken token)
{
Subsriber<Message> subsriber = new Subsriber<Message>()
{
Interval = 1000
};
subsriber.Callback(Process, m => m != null);
}
private void Process()
{
//do work
}
}
Hope this gives you an idea of how else you can approach your problem and that I didn't miss the point :).
Update
To use events to update progress or which tasks are processing, I'd extract them into their own class, which then has subscribe methods on it, and when creating a new instance of that class, assign the event to a handler in the parent class which can then update your UI or whatever you want it to do with that info.
So the content of Process() would look more like this:
Processor processor = new Processor();
Task task = Task.Factory.StartNew(() => processor.ProcessMessage(cancellationTokenSource.CancellationToken));
processor.StatusUpdated += ReportProcess;

Return partial result from async method

I have a class that calls a web service to retrieve data asynchronously. To improve performance I implemented a client-side cache which checks if the requested data is available locally. The class returns all data that is stored in the cache and calls the web service to fetch the remaining data.
Can I return the cached data to the caller and then proceed with the web call, or do I have to make the call and return the complete set of data?
In a synchronous environment, I could use yield return, by with Tasks and async/await yield is not possible.
How can I solve this problem?
You can use Observable
// in your UI
var result = new ObservableCollection<Data>(); // this is your list to bind to UI
IsBusy = true; // indicate that work is done
yourDataSource.GetData(2000).Subscribe(batch =>
{
foreach (var d in batch)
{
result.Add(d);
}
},
exception =>
{
Log.Error(exception);
IsBusy = false;
},
() =>
{
// this means done
IsBusy = false;
}
)
// or you can await the whole thing
try
{
IsBusy = true;
await yourDataSource.GetData(5).Do(batch =>
{
foreach (var d in batch)
{
result.Add(d);
}
});
}
finally
{
IsBusy = false;
}
Your data source:
IObservable<IList<Data>> GetData(int args)
{
var result = new Subject<IList<Data>>();
Task.Run(async () =>
{
try
{
var formCache = await GetFromCache(args);
result.OnNext(fromCache);
while (moreBatches)
{
result.OnNext(GetNextBatch(args));
}
result.OnCompleted();
}
catch (Exception e)
{
result.OnError(e);
}
});
return result;
}
If you are using WPF, Xamarin.Forms or UWP, I strongly recommend ReactiveCommand which can return observable and do the whole IsBusy thing for you.
None of the suggestions fit my problem perfectly, but a combination of them did.
At first, I created a pipeline with the TPL DataFlow library to fetch the data from my sources. The results were then posted into a BufferBlock<T> at the end, that was exposed to the end user as an IObservable<T>. This had the added benefit that I now can easily query related data via additional blocks.

Async/Await - Await not holding as expected

When performing a long running operation I noticed that i could kickstart a long running sub-operation right off the start line and do other stuff while it fetches results from caches/databases.
The given operation is:
public async Task<Fichaclis> Finalize()
{
using (TransactionScope transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
transactionTimer.Start();
var agendasTransitionTask = ExecuteAgendas();
... DO ALOT OF SYNC OPERATIONS ...
await agendasTransitionTask;
transaction.Complete();
}
}
private Task ExecuteAgendas()
{
return ags.GetAgendas().ContinueWith((prev) =>
{
var currentAgendas = prev.Result;
foreach (var item in currentAgendas)
{
... DO QUICK SYNC STUFF...
}
return ags.BulkEditAgenda(currentAgendas);
});
}
GetAgendas is a method used all over with the following signature:
public async Task<List<Agendas>> GetAgendas()
because it's widely used, i believe the problem is not there. As for BulkEditAgenda:
public async Task BulkEditAgenda(IEnumerable<Agendas> agendas)
{
if (agendas == null || agendas.Count() == 0)
{
return;
}
var t1 = AddOrUpdateCache(agendas);
var t2 = Task.Factory.StartNew(() =>
{
try
{
foreach (var item in agendas)
{
EditNoReconnection(item);
}
Save();
}
catch (Exception ex)
{
//log
throw;
}
});
await Task.WhenAll(t1, t2);
}
EditNoReconnect and Save are both sync methods.
private Task AddOrUpdateCache(IEnumerable<Agendas> agendas)
{
var tasks = new List<Task>();
foreach (var item in agendas)
{
tasks.Add(TryGetCache(item)
.ContinueWith((taskResult) =>
{
...DO QUICK SYNC STUFF...
})
);
}
return Task.WhenAll(tasks);
}
TryGetCache is also a widely used method, so I think it's safe... it's signature is private Task<AgendasCacheLookupResult> TryGetCache(
So, resuming the issue at hand: For a small set of items do in the sync session of the Finalize method the command transaction.Complete() is execute before Save() (inside BulkEditAgendas). For a regular or large amount of items, it works as expected.
This means that i'm not chaining the Tasks correctly, or my understanding of how Async/Await + Tasks/ContinueWith works is fundamentally incorrect. Where am I wrong?
The problem is most likely here:
private Task ExecuteAgendas()
{
return ags.GetAgendas().ContinueWith((prev) =>
{
var currentAgendas = prev.Result;
foreach (var item in currentAgendas)
{
... DO QUICK SYNC STUFF...
}
return ags.BulkEditAgenda(currentAgendas);
});
}
First, what you return from this is the continuation task (result of ContinueWith). But body of ContinueWith ends when you do
return ags.BulkEditAgenda(currentAgendas);
So body of continuation ends potentially before BulkEditAgenda task is completed (you don't wait in any way for completion of BulkEditAgenda). So this line
await agendasTransitionTask;
Returns while BulkEditAgenda is still in progress. To clarify even more, note that what is returned from ExecuteAgendas is Task<Task> and result of await agendasTransitionTask is Task which represents your running BulkEditAgenda.
To fix, just use async\await like you do everywhere else:
private async Task ExecuteAgendas() {
var currentAgengas = await ags.GetAgendas();
foreach (var item in currentAgendas) {
// do stuff
}
await ags.BulkEditAgenda(currentAgendas);
}

Cancel Operations / UI Notification and UI Information?

I'm currently working on a small project that use Tasks.Dataflow and I'm a little bit confused about UI notifications. I want to separate my "Pipeline" from the UI in another class called PipelineService, but I'm unable to notify the UI on cancelled operations or data that should be shown up in the UI. How can this be handled in the right manner?
Code:
private void btnStartPipeline_Click(object sender, EventArgs e)
{
btnStartPipeline.Enabled = false;
btnStopPipeline.Enabled = true;
cancellationToken = new CancellationTokenSource();
if (head == null)
{
head = pipeline.SearchPipeline();
}
head.Post(AppDirectoryNames.STORE_PATH);
}
private void btnStopPipeline_Click(object sender, EventArgs e)
{
cancellationToken.Cancel();
}
This methods related to Form1.cs. head is type of ITargetBlock<string>.
public ITargetBlock<string> SearchPipeline()
{
var search = new TransformBlock<string, IEnumerable<FileInfo>>(path =>
{
try
{
return Search(path);
}
catch (OperationCanceledException)
{
return Enumerable.Empty<FileInfo>();
}
});
var move = new ActionBlock<IEnumerable<FileInfo>>(files =>
{
try
{
Move(files);
}
catch (OperationCanceledException ex)
{
throw ex;
}
});
var operationCancelled = new ActionBlock<object>(delegate
{
form.Invoke(form._update);
},
new ExecutionDataflowBlockOptions
{
TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
});
search.LinkTo(move);
search.LinkTo(operationCancelled);
return search;
}
Invoke don't take effect with delegate methods. What am I doing wrong here?
At first, I din't understand why you think your code should work. The way you set up your dataflow network, each IEnumerable<FileInfo> generated by the search block is first sent to the move block. If the move block didn't accept it (which never happens here), it would be sent to the operationCancelled block. That doesn't seem to be what you want at all.
After looking at the walkthough you seem to be basing your code on, it does cancellation similar than you, but with one significant difference: it uses LinkTo() with a predicate, which rejects a message that signifies cancellation. If you wanted to do the same, you would need to also use LinkTo() with a predicate. And since I don't think an empty sequence is a good choice to signify cancellation, I think you should switch to null too.
Also, you don't need to use form.Invoke() if you're already using TaskScheduler.FromCurrentSynchronizationContext(), they do basically the same thing.
public ITargetBlock<string> SearchPipeline()
{
var search = new TransformBlock<string, IEnumerable<FileInfo>>(path =>
{
try
{
return Search(path);
}
catch (OperationCanceledException)
{
return null;
}
});
var move = new ActionBlock<IEnumerable<FileInfo>>(files =>
{
try
{
Move(files);
}
catch (OperationCanceledException)
{
// swallow the exception; we don't want to fault the block
}
});
var operationCancelled = new ActionBlock<object>(_ => form._update(),
new ExecutionDataflowBlockOptions
{
TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
});
search.LinkTo(move, files => files != null);
search.LinkTo(operationCancelled);
return search;
}

Using async/await with Live SDK

From what I've seen about using the Async CTP with the event asynchronous pattern, the code I have here should work fine, with var result1 = await tcs1.Task blocking until clientGetFileList.GetCompleted fires. However, what ends up happening is that I get bounced back to GetRestoreStream, on return GetRestoreStreamAwait().Result which never does return -- instead, my app pretty much locks up on me.
Can someone please explain to me what it is I am doing wrong?
protected override Stream GetRestoreStream()
{
if (SkyDriveFolderId != null)
return GetRestoreStreamAwait().Result;
return Stream.Null;
}
private async Task<Stream> GetRestoreStreamAwait()
{
LiveConnectClient clientGetFileList = new LiveConnectClient(_session);
TaskCompletionSource<LiveOperationCompletedEventArgs> tcs1 = new TaskCompletionSource<LiveOperationCompletedEventArgs>();
EventHandler<LiveOperationCompletedEventArgs> d1 = (o, e) => { tcs1.TrySetResult(e); };
clientGetFileList.GetCompleted += d1;
clientGetFileList.GetAsync(SkyDriveFolderId + "/files");
var result1 = await tcs1.Task;
clientGetFileList.GetCompleted -= d1;
// ... method continues for a while
}
Update: This bit of code seems to move through all the way, but task.Start() tosses off an InvalidOperationException so I never actually get the stream at the end. Wrapping it in a try/catch doesn't change anything, either -- without the try/catch the InvalidOperationException is caught further up the stack while the operation runs happily ignorant of the fact its result will never be used; with it, task.Result freezes things just as surely as the code above.
protected override Stream GetRestoreStream()
{
if (SkyDriveFolderId != null)
{
var task = GetRestoreStreamImpl();
task.Start();
return task.Result;
}
return Stream.Null;
}
private async Task<Stream> GetRestoreStreamImpl()
{
var getResult = await GetTaskAsync(SkyDriveFolderId + "/files");
List<object> data = (List<object>)getResult["data"];
foreach (IDictionary<string, object> dictionary in data)
{
if (dictionary.ContainsKey("name") && (string)dictionary["name"] == BackupFileName)
{
if (dictionary.ContainsKey("id"))
{
SkyDriveFileId = (string)dictionary["id"];
break;
}
}
}
if (String.IsNullOrEmpty(SkyDriveFileId))
{
MessageBox.Show("Restore failed: could not find backup file", "Backup", MessageBoxButton.OK);
return Stream.Null;
}
return await DownloadTaskAsync(SkyDriveFileId + "/content");
}
private Task<IDictionary<string,object>> GetTaskAsync(string path)
{
var client = new LiveConnectClient(_session);
var tcs = new TaskCompletionSource<IDictionary<string, object>>();
client.GetCompleted += (o, e) =>
{
if (e.Error != null)
tcs.TrySetException(e.Error);
else if (e.Cancelled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(e.Result);
};
client.GetAsync(path);
return tcs.Task;
}
private Task<Stream> DownloadTaskAsync(string path)
{
var client = new LiveConnectClient(_session);
var tcs = new TaskCompletionSource<Stream>();
client.DownloadCompleted += (o, e) =>
{
if (e.Error != null)
tcs.TrySetException(e.Error);
else if (e.Cancelled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(e.Result);
};
client.DownloadAsync(path);
return tcs.Task;
}
You are misunderstanding the way that async/await works. Basically, your code is blocking at the var result1 and below. However, what await allows is for the code that called the async method (GetRestoreStream in this case) to be returned to as soon as a long running task* with await in front of it is called. If you were not reliant on the .Result, then your GetRestoreStream method would complete. However, since you require the result, then your GetRestoreStream method becomes synchronous while it waits for GetRestoreStreamAwait to complete. I will add some visuals shortly.
Here is some sample code flow:
-GetRestoreStream calls GetRestoreStreamAwait
---GetRestoreStreamAwait calls an async task
-GetRestoreStreamAwait returns to GetRestoreStream with a pending result
-GetRestoreStream can do anything it wants, but if it calls for the pending result, it will block
---GetRestoreStreamAwait finally finishes its async task and continues through its code, returning a result
-Any code in GetRestoreStream that was waiting for the result receives the Result
This is not the best graphical representation, hopefully it helps explain it a little though. The thing to notice is that the code flow is not what you are used to due to the nature of async
So, my guess is that your app locks up only because you are trying to access the Result that is not available yet, and all you should have to do is wait for the tcs1.Task to complete. If you want to avoid locking up, then you will need to nest the call so that GetRestoreStream is also an async method. However, if the result is what you are ultimately looking for, then you will need to wait for the return, or simply set up a callback like you normally would for the async pattern
*Notice that I said long running task because the compiler will not waste time rewriting code that is already completed (if it is indeed completed by the time the await is called)
UPDATE...try this
protected override Stream GetRestoreStream()
{
if (SkyDriveFolderId != null)
return GetRestoreStreamAwait().Result;
return Stream.Null;
}
private async Task<Stream> GetRestoreStreamAwait()
{
try
{
LiveConnectClient clientGetFileList = new LiveConnectClient(_session);
TaskCompletionSource<LiveOperationCompletedEventArgs> tcs1 = new TaskCompletionSource<LiveOperationCompletedEventArgs>();
EventHandler<LiveOperationCompletedEventArgs> d1 =
(o, e) =>
{
try
{
tcs1.TrySetResult(e);
}
catch(Exception ex)
{
tcs1.TrySetResult(null);
}
};
clientGetFileList.GetCompleted += d1;
clientGetFileList.GetAsync(SkyDriveFolderId + "/files");
var result1 = await tcs1.Task;
clientGetFileList.GetCompleted -= d1;
// ... method continues for a while
}
catch(Exception ex)
{
return null;
}
}

Categories