Expression parameter reference - c#

I've created a download manager, but as far as download manager stores the download arguments, it's working good.
Downloader downloader = new Downloader();
downloader.Add(new List<Query>(){
new Query(dataset.trans_sprzedaz_pozycje, () => dataset.PobierzTransSprzedazPozycja(downloader.Filter)),
new Query(dataset.trans_sprzedaz_rabaty_dodatkowe, () => dataset.PobierzDokumentySprzedazyRabaty(downloader.Filter)),
new Query(dataset.trans_sprzedaz_teksty, () => dataset.PobierzDokumentySprzedazyTeksty(downloader.Filter))
});
But in some cases I need my queries to store the arguments, so it should look like:
Downloader downloader = new Downloader();
downloader.Add(new List<Query>(){
new Query(dataset.trans_sprzedaz_pozycje, () => dataset.PobierzTransSprzedazPozycja(query.Filter)),
new Query(dataset.trans_sprzedaz_rabaty_dodatkowe, () => dataset.PobierzDokumentySprzedazyRabaty(query.Filter)),
new Query(dataset.trans_sprzedaz_teksty, () => dataset.PobierzDokumentySprzedazyTeksty(query.Filter))
});
Note that in first code snippet I use downloader.Filter, in second I use (fabricated) query.Filter
I know that I can make constructions like:
var query = new Query(dsSprzedaz.trans_sprzedaz_pozycje);
query.AddFunc(() => dsSprzedaz.PobierzTransSprzedazPozycja(query.Filter));
But adding 20-30 queries like this would be terrible.
I tried sollution like this:
Filter filter;
new Query(dsSprzedaz.trans_sprzedaz_pozycje,() => dsSprzedaz.PobierzTransSprzedazPozycja(filter), out filter),
but it copy value from query.Filter, not the reference.
//EDIT
Here's the most important code of downloader:
private Task Execute()
{
var Tasks = new List<Task>(Queries.Count);
foreach (var query in Queries)
{
var task = query.Execute(CancellationToken);
Tasks.Add(task);
}
return Task.WhenAll(Tasks);
}
private void CreateFilter(List<long> id_list)
{
lock (Data)
{
Data.Clear();
Data.Append("(0");
foreach (var value in id_list)
Data.Append("," + value);
Data.Append(")");
}
}
public string Filter
{
get
{
return Data.ToString();
}
}
And Query:
public class Query
{
DataTable Source;
Func<DataTable> Download;
StringBuilder Data;
public Query(DataTable Source, Expression<Func<DataTable>> Download, out string filter)
{
this.Source = Source;
this.Data = new StringBuilder();
filter = Filter;
this.Download = Download.Compile();
}
public async Task Execute(CancellationToken cancellationToken)
{
try
{
DataTable result = await Task.Run(() => Download());
if (cancellationToken.IsCancellationRequested) return;
Source.Merge(result);
}
catch (Exception) { }
}
private void CreateFilter(List<long> id_list)
{
lock (Data)
{
Data.Clear();
Data.Append("(0");
foreach (var value in id_list)
Data.Append("," + value);
Data.Append(")");
}
}
public string Filter
{
get
{
return Data.ToString();
}
}
}
`

Make Query store information if it should use it's own filter (you said that you want to use it's filter only some times, in other cases you want to use Downloader.Filter)
public class Query
{
DataTable Source;
Func<string, DataTable> Download;
StringBuilder Data;
public bool IsOwnFilter { get; set; }
public Query(DataTable Source, Func<string, DataTable> Download, bool isOwnFilter = false)
{
this.Source = Source;
this.Data = new StringBuilder();
this.Download = Download;
this.IsOwnFilter = isOwnFilter;
}
public async Task Execute(strign filter, CancellationToken cancellationToken)
{
try
{
DataTable result = await Task.Run(() => Download(filter));
if (cancellationToken.IsCancellationRequested) return;
Source.Merge(result);
}
catch (Exception) { }
}
// This is not used in your code
private void CreateFilter(List<long> id_list)
{
lock (Data)
{
Data.Clear();
Data.Append("(0");
foreach (var value in id_list)
Data.Append("," + value);
Data.Append(")");
}
}
public string Filter
{
get
{
return Data.ToString();
}
}
}
Next when you call it from Downloader, check for new flag and pass correct filter:
private Task Execute()
{
var Tasks = new List<Task>(Queries.Count);
foreach (var query in Queries)
{
// call Execute with Query filter or Downloader.Filter
var task = query.Execute(query.IsOwnFilter ? query.Filter : Filter, CancellationToken);
Tasks.Add(task);
}
return Task.WhenAll(Tasks);
}
And when you add queries to Downloader:
Downloader downloader = new Downloader();
downloader.Add(new List<Query>(){
new Query(dataset.trans_sprzedaz_pozycje, (filter) => dataset.PobierzTransSprzedazPozycja(filter)),
new Query(dataset.trans_sprzedaz_rabaty_dodatkowe, (filter) => dataset.PobierzDokumentySprzedazyRabaty(filter)),
new Query(dataset.trans_sprzedaz_teksty, (filter) => dataset.PobierzDokumentySprzedazyTeksty(filter))
});

Related

Losing results with Task.WaitAll() not waiting until actual completion

I'm implementing the MailChimp.NET wrapper in both synchronous and asynchronous ways and calls are going through without a problem, BUT results tend to get lost in the synchronous methods. In other words, if I send 100 members to be added (by batches of 10 due to the simultaneous connections limit of the MailChimp API), all 100 will indeed be visible in my MC audience but I'll loose from 5 to 25% of the results on code side. Here's the concerned bit of my implementation :
public class MailChimpClient : IDisposable
{
private MailChimpManager _mcm;
private string _apiKey;
private bool _isDisposed;
private ConcurrentQueue<MailChimpMember> _updatedMembersQueue;
private ConcurrentQueue<MailChimpBaseException> _exceptionsQueue;
private const int BatchSize = 10;
private const int TaskDelay = 100;
private ConcurrentQueue<MailChimpMember> UpdatedMembersQueue
{
get { return _updatedMembersQueue = _updatedMembersQueue ?? new ConcurrentQueue<MailChimpMember>(); }
set { _updatedMembersQueue = value; }
}
private ConcurrentQueue<MailChimpBaseException> ExceptionsQueue
{
get { return _exceptionsQueue = _exceptionsQueue ?? new ConcurrentQueue<MailChimpBaseException>(); }
set { _exceptionsQueue = value; }
}
public MailChimpClient(string apiKey)
{
_apiKey = apiKey;
_mcm = new MailChimpManager(apiKey);
}
private async Task AddOrUpdateMember(MailChimpMember member, string listId)
{
try
{
var model = member.ToApiMember();
model = await _mcm.Members.AddOrUpdateAsync(listId, model);
UpdatedMembersQueue.Enqueue(new MailChimpMember(model));
await Task.Delay(TaskDelay);
}
catch (Exception ex)
{
var mccex = new MailChimpClientException($"Error adding/updating member \"{(member != null ? member.MailAddress.ToString() : "NULL")}\" to list with ID \"{listId}\".", ex);
ExceptionsQueue.Enqueue(mccex);
}
}
private MailChimpClientResult AddOrUpdateMemberRange(IEnumerable<MailChimpMember> members, string listId)
{
var batches = members.GetBatches(BatchSize);
var result = new MailChimpClientResult();
var i = 0;
foreach (var batch in batches)
{
AddOrUpdateMemberBatch(batch, listId);
i++;
FlushQueues(ref result);
}
return result;
}
private void AddOrUpdateMemberBatch(MailChimpMember[] batch, string listId)
{
Task.WaitAll(batch.Select(async b => await AddOrUpdateMember(b, listId)).ToArray(), -1);
}
private void FlushQueues(ref MailChimpClientResult result)
{
result.UpdatedMembers.FlushQueue(UpdatedMembersQueue);
result.Exceptions.FlushQueue(ExceptionsQueue);
}
public MailChimpClientResult AddOrUpdate(MailChimpMember member, string listId)
{
return AddOrUpdateMemberRange(new MailChimpMember[] { member }, listId);
}
public MailChimpClientResult AddOrUpdate(IEnumerable<MailChimpMember> members, string listId)
{
return AddOrUpdateMemberRange(members, listId);
}
}
public static class CollectionExtensions
{
public static T[][] GetBatches<T>(this IEnumerable<T> items, int batchSize)
{
var result = new List<T[]>();
var batch = new List<T>();
foreach (var t in items)
{
if (batch.Count == batchSize)
{
result.Add(batch.ToArray());
batch.Clear();
}
batch.Add(t);
}
result.Add(batch.ToArray());
batch.Clear();
return result.ToArray();
}
public static void FlushQueue<T>(this IList<T> list, ConcurrentQueue<T> queue)
{
T item;
while (queue.TryDequeue(out item))
list.Add(item);
}
}
MailChimpMember being a public copy of the MailChimp.NET Member. The problem seems to happen in the batch processing method, the Task.WaitAll(...) instruction firing its completion event before all calls are complete, therefore not all results are queued. I tried delaying the execution of each individual treatment with Task.Delay() but with little to no result.
Does anyone have an idea what is failing in my implementation ?

async await execution order problems with httpclient

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

SyncContext is not yet initialized error when trying to GET from Azure easy table

I creating a UWP app that pulls in data from an easy table using IMobileServiceSyncTable, but when I try and use this to get information from the table to put into a list I get a SyncContext is not yet initialized error. I can add items to the database fine its just receiving things thats giving me trouble.
Heres my code for interacting with the table:
private MobileServiceCollection<DrillItem, DrillItem> drills;
private IMobileServiceSyncTable<DrillItem> drillTable = App.MobileService.GetSyncTable<DrillItem>();
public CombatDrillsTable()
{
}
public MobileServiceCollection<DrillItem, DrillItem> GetDrills()
{
return this.drills;
}
public async Task AddDrill(DrillItem drillItem, String n, int s, int t, string sty)
{
drillItem.Name = n;
drillItem.Sets = s;
drillItem.SetTime = t;
drillItem.Style = sty;
await App.MobileService.GetTable<DrillItem>().InsertAsync(drillItem);
drills.Add(drillItem);
}
public async void GetById(string n)
{
IMobileServiceTableQuery<DrillItem> query = drillTable.Where(drillItem => drillItem.Name == n)
.Select(drillItem => drillItem);
List<DrillItem> items = await query.ToListAsync();
Console.WriteLine(items);
}
public async Task GetDrillsAsync(String cat)
{
MobileServiceInvalidOperationException exception = null;
try {
drills = await drillTable.Where(drillItem => drillItem.Style == cat)
.ToCollectionAsync();
Console.WriteLine(drills);
}
catch (MobileServiceInvalidOperationException e)
{
exception = e;
}
if (exception != null)
{
await new MessageDialog(exception.Message, "Error loading items").ShowAsync();
}
else
{
// code here
}
}
heres the code that activates the get:
String parameters;
CombatTableView ctv = new CombatTableView();
private ObservableCollection<DrillItem> _items;
private ObservableCollection<DrillItem> _temp;
public DisplayDrills()
{
this.InitializeComponent();
_items = new ObservableCollection<DrillItem>();
AddItemsAsync();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
parameters = (String)e.Parameter;
testBox.Text = parameters;
RefreshListView.ItemsSource = _items;
}
private async Task updateDrillsAsync(){
await ctv.combatDrillsTable.GetDrillsAsync(parameters);
}
private async void AddItemsAsync()
{
await updateDrillsAsync();
_temp = ctv.combatDrillsTable.GetDrills();
foreach (var t in _temp)
{
_items.Insert(0, t);
}
}
As the exception described, we must initialize the local SQLite database before we can use the offline client. Details please reference Configuring the Local SQLite database. For example:
private async Task InitLocalStoreAsync()
{
if (!App.MobileService.SyncContext.IsInitialized)
{
var store = new MobileServiceSQLiteStore("localstore.db");
store.DefineTable<TodoItem>();
await App.MobileService.SyncContext.InitializeAsync(store);
}
await SyncAsync();
}
private async Task SyncAsync()
{
await App.MobileService.SyncContext.PushAsync();
await todoTable.PullAsync("todoItems", todoTable.CreateQuery());
}
And the best place to do this is in the GetTable<> method, the following code showed the example for initialize it when OnNavigatedTo, :
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
await InitLocalStoreAsync(); // offline sync
ButtonRefresh_Click(this, null);
}

How to make different calls to an async method waiting for the result of first call?

Let's say I have a method it gets data from server
Task<Result> GetDataFromServerAsync(...)
If there is an ongoing call in progress, I don't want to start a new request to server but wait for the original to finish.
Let's say I have
var result = await objet.GetDataFromServerAsync(...);
and in a different place, called almost at the same time I have a second call
var result2 = await objet.GetDataFromServerAsync(...);
I don't want the second to start a new request to server if the first didn't finish. I want both calls to get the same result as soon as first call finish. This is a proof of concept, I have options but I wanted to see how easy it's to do this.
Here is a quick example using Lazy<Task<T>>:
var lazyGetDataFromServer = new Lazy<Task<Result>>
(() => objet.GetDataFromServerAsync(...));
var result = await lazyGetDataFromServer.Value;
var result2 = await lazyGetDataFromServer.Value;
It doesn't matter if these 2 awaits are done from separate threads as Lazy<T> is thread-safe, so result2 if ran second will still wait and use the same output from result.
Using the code from here you can wrap this up in a class called AsyncLazy<T>, and add a custom GetAwaiter so that you can just await it without the need to do .Value, very tidy =)
You can use anything for syncrhonization in your method.
For example, I used SemaphoreSlim:
public class PartyMaker
{
private bool _isProcessing;
private readonly SemaphoreSlim _slowStuffSemaphore = new SemaphoreSlim(1, 1);
private DateTime _something;
public async Task<DateTime> ShakeItAsync()
{
try
{
var needNewRequest = !_isProcessing;
await _slowStuffSemaphore.WaitAsync().ConfigureAwait(false);
if (!needNewRequest) return _something;
_isProcessing = true;
_something = await ShakeItSlowlyAsync().ConfigureAwait(false);
return _something;
}
finally
{
_isProcessing = false;
_slowStuffSemaphore.Release();
}
}
private async Task<DateTime> ShakeItSlowlyAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
return DateTime.UtcNow;
}
}
Usage:
var maker = new PartyMaker();
var tasks = new[] {maker.ShakeItAsync(), maker.ShakeItAsync()};
Task.WaitAll(tasks);
foreach (var task in tasks)
{
Console.WriteLine(task.Result);
}
Console.WriteLine(maker.ShakeItAsync().Result);
Here is result:
17.01.2017 22:28:39
17.01.2017 22:28:39
17.01.2017 22:28:41
UPD
With this modification you can call async methods with args:
public class PartyMaker
{
private readonly SemaphoreSlim _slowStuffSemaphore = new SemaphoreSlim(1, 1);
private readonly ConcurrentDictionary<int, int> _requestCounts = new ConcurrentDictionary<int, int>();
private readonly ConcurrentDictionary<int, DateTime> _cache = new ConcurrentDictionary<int, DateTime>();
public async Task<DateTime> ShakeItAsync(Argument argument)
{
var key = argument.GetHashCode();
DateTime result;
try
{
if (!_requestCounts.ContainsKey(key))
{
_requestCounts[key] = 1;
}
else
{
++_requestCounts[key];
}
var needNewRequest = _requestCounts[key] == 1;
await _slowStuffSemaphore.WaitAsync().ConfigureAwait(false);
if (!needNewRequest)
{
_cache.TryGetValue(key, out result);
return result;
}
_cache.TryAdd(key, default(DateTime));
result = await ShakeItSlowlyAsync().ConfigureAwait(false);
_cache[key] = result;
return result;
}
finally
{
_requestCounts[key]--;
if (_requestCounts[key] == 0)
{
int temp;
_requestCounts.TryRemove(key, out temp);
_cache.TryRemove(key, out result);
}
_slowStuffSemaphore.Release();
}
}
private async Task<DateTime> ShakeItSlowlyAsync()
{
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
return DateTime.UtcNow;
}
}
public class Argument
{
public Argument(int value)
{
Value = value;
}
public int Value { get; }
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

can a method with return type as list be called from a thread

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);
}
}
});
}
}
}

Categories

Resources