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.
Related
I'm fairly new to tasks and want to implement them on my application. What I want is to do an ajax request on my client side when the page loads, to a function that calls all the catalogs that I need and returns a JSON of all the objects to my client. This function on the server side is the one I created to have multiple tasks. So I have the following questions regarding this:
Is it a good practice to load all catalogs needed for that page and return a JSON object?
Does it actually work as multi thread if the tasks call the same instance of a class? Or is it better to create the instance inside each task?
public JsonResult GetCatalogs()
{
JsonResult jSonResult = new JsonResult();
try
{
CatalogsRepository catalogsRepository = new CatalogsRepository();
Task<IList<CustomObject1>> task1 = Task.Factory.StartNew(() =>
{
IList<CustomObject1> resultList1 = catalogsRepository.getFirstCatalog();
return resultList1;
});
Task<IList<CustomObject2>> task2 = Task.Factory.StartNew(() =>
{
IList<CustomObject2> resultList2 = catalogsRepository.getSecondCatalog();
return resultList2;
});
Task<IList<CustomObject3>> task3 = Task.Factory.StartNew(() =>
{
IList<CustomObject3> resultList3 = catalogsRepository.getThirdCatalog();
return resultList3;
});
jSonResult = Json(new
{
result1 = task1.Result,
learningMaterialTypeList = task2.Result,
contentAssociatedList = task13.Result
});
jSonResult.MaxJsonLength = int.MaxValue;
jSonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
}
catch (Exception ex)
{
log.Error(ex);
return Json(new Error { messageCode = 1, message = ex.Message });
}
return jSonResult;
}
Or is it better to create the instance inside each task?
public JsonResult GetCatalogs()
{
JsonResult jSonResult = new JsonResult();
try
{
Task<IList<CustomObject1>> task1 = Task.Factory.StartNew(() =>
{
CatalogsRepository catalogsRepository = new CatalogsRepository();
IList<CustomObject1> resultList1 = catalogsRepository.getFirstCatalog();
return resultList1;
});
Task<IList<CustomObject2>> task2 = Task.Factory.StartNew(() =>
{
CatalogsRepository catalogsRepository = new CatalogsRepository();
IList<CustomObject2> resultList2 = catalogsRepository.getSecondCatalog();
return resultList2;
});
Task<IList<CustomObject3>> task3 = Task.Factory.StartNew(() =>
{
CatalogsRepository catalogsRepository = new CatalogsRepository();
IList<CustomObject3> resultList3 = catalogsRepository.getThirdCatalog();
return resultList3;
});
jSonResult = Json(new
{
result1 = task1.Result,
learningMaterialTypeList = task2.Result,
contentAssociatedList = task13.Result
});
jSonResult.MaxJsonLength = int.MaxValue;
jSonResult.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
}
catch (Exception ex)
{
log.Error(ex);
return Json(new Error { messageCode = 1, message = ex.Message });
}
return jSonResult;
}
I'm using MVC .NET 4.0 with jQuery, Thank you in advance.
Your action method should be async:
public async Task<JsonResult> GetCatalogs()
Once your method is async, everything else is much easier. You can do away with those Task.Run() calls and just call the methods normally (as long as they as async) and await them. It may make sense to make those lambdas into separate, async methods (or even C#7 local methods):
protected async Task<IList<CustomObject1>> GetResults1()
{
CatalogsRepository catalogsRepository = new CatalogsRepository();
var resultList1 = catalogsRepository.getFirstCatalog();
return await resultList1.ToListAsync();
});
Important: To get results you should use await. Never use Result in an async ASP.NET environment because it will deadlock.
var jsonResult = Json(new
{
result1 = await task1,
learningMaterialTypeList = await task2,
contentAssociatedList = await task3
});
As for your questions:
Is it a good practice to load all catalogs needed for that page and return a JSON object?
There is no rule against it. It's a design decision for you to make. The pro is fewer round trips, the con is coupling all those things together in a public interface makes the architecture a bit more brittle.
Does it actually work as multi thread if the tasks call the same instance of a class? Or is it better to create the instance inside each task?
In true multithreading, it is better in general to work on separate instances so that each thread can have its own independent memory cache. That being said, async is not the same as multithreading, and in fact in ASP.NET you're guaranteed that no two of your tasks will be executing simultaneously (unless you do something special to make that happen). Instead you get one thread at a time. The thread might change, and your async code will jump around await points, but only one piece of code gets to run at once, by design.
1.I think that by returning a JSON object you mean an action which returns a JsonObject and is called by ajax from the client side. You can do this in most places of your app but keep in mind that you can lose a lot of benefits from pure MVC. The second thing is jQuery and Ajax are a bit hard to maintain and in case of complex views you will have to write a lot of code, very often hard to test.
A very common concept in asp.net MVC is returning ViewModels/Models via GET methods and then sending data from forms with the use of asp.net helpers back to the controllers. If you use data annotation attributes and jQuery unobtrusive library you can build really nice validation, very easy to maintain on the backend and on the client side. The client-side validation will be generated just for FREE based on the .net attributes which shorten your time in development. With Ajax calls, you can do similar but the code will be more clunky.
2 and 3.Your controller should contain as less business logic as possible and all the logic should be moved to other layers, services etc. Your actual code is over too complicated.
Try to use interfaces and IOC. Avoid creating instances of objects like you do now in the controller. It’s hard to write tests for such a class.
I think something like this would be better:
private readonly ICatalogService _catalogService;
// Dependency injection thanks to Inversion of Control
public BlahController(ICatalogService catalogService){
_catalogService=catalogService;
}
public async JsonResult GetCatalogs(params..){
return await catalogService.getCatalogs(params..); //or catalogRepository
}
So you can move all the business logic out of the controller to some separate class.
I must synchronize data coming from WCF WebServices in a SQLite database.
This synchronization represents a dozen of WebServices, that can be "grouped" in 4 categories:
"User's" Rights
"Forms" data, which can be updated from the two sides (user/server)
"Server" data, which are only updated from the server
"Views" data, wich copy locally the views of the server
Each call to the WebService is done through HttpClient:
response = await client.PostAsync(webServiceName, content);
Each WebService has its own async method where the WebService response is deserialized:
public static async Task<string> PushForm(List<KeyValuePair<string, string>> parameters)
{
var response = await JsonParser.GetJsonFromUrl(WebServiceName.PushForm.Value, parameters);
Forms forms = new Forms();
try
{
forms = JsonConvert.DeserializeObject<Forms>(response);
return response;
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
Then I have a SynchronizationService class, where I regroup the calls to WebServices by categories:
public async Task<bool> SynchronizeServerData()
{
bool result = false;
try
{
result = true;
List<Table1> tables1 = await WebServices.GetListTable1(null);
if (tables1 != null)
{
ServiceLocator.Current.GetInstance<IRepository>().DeleteAll<Table1>(true);
ServiceLocator.Current.GetInstance<IRepository>().AddAll<Table1>(tables1);
}
List<Table2> tables2 = await WebServices.GetListTable2(null);
if (tables2 != null)
{
ServiceLocator.Current.GetInstance<IRepository>().DeleteAll<Table2>(true);
ServiceLocator.Current.GetInstance<IRepository>().AddAll<Table2>(tables2);
}
List<Table3> tables3 = await WebServices.GetListTable3(null);
if (tables3 != null)
{
ServiceLocator.Current.GetInstance<IRepository>().DeleteAll<Table3>(true);
ServiceLocator.Current.GetInstance<IRepository>().AddAll<Table3>(tables3);
}
...
}
catch (Exception e)
{
result = false;
}
return result;
}
And finally, in the main ViewModel, I call each of these methods:
public async void SynchronizeData(bool firstSync)
{
IsBusy = true;
var resUsers = await _synchronisation.SynchronizeUsersRights();
var resServer = await _synchronisation.SynchronizeServerData();
var resForm = await _synchronisation.SynchronizeForms();
var resViews = await _synchronisation.SynchronizeViews();
IsBusy = false;
}
But due to the use of "await" the performance is not good.
=> I would like to know if there is an easy way to "parallelize" the calls to optimize performance? Or is it possible to separate the data recovery from the SQLite update for this?
On the face of it there appear to be opportunities to run a few things concurrently, for instance in SynchronizeData you could do this instead:
{
IsBusy = true;
Task resUsersTask = _synchronisation.SynchronizeUsersRights();
Task resServerTask = _synchronisation.SynchronizeServerData();
Task resFormTask = _synchronisation.SynchronizeForms();
Task resViewsTask = _synchronisation.SynchronizeViews();
await Task.WhenAll(resUsersTask, resServerTask, resFormTask, resViewsTask);
var resUsers = resUsersTask.Result;
var resServer = resServerTask.Result;
var resForm = resFormsTask.Result;
var resViews = resViewsTask.Result;
IsBusy = false;
}
...which would allow those 4 tasks to run concurrently.
You could do the same in SynchronizeServerData; e.g.:
result = true;
Task tables1Task = WebServices.GetListTable1(null);
Task tables2Task = WebServices.GetListTable2(null);
Task tables3Task = WebServices.GetListTable3(null);
List<Table1> tables1 = await tables1Task;
// ...
List<Table2> tables2 = await tables2Task;
// ...
List<Table3> tables3 = await tables3Task;
// ...
This would allow the 3 tasks to run concurrently as well.
How much you actually gain from this might depend on things like does SQLite allow you to do multiple concurrent requests - I don't know the answer to that offhand.
Some other comments:
public async void SynchronizeData(bool firstSync)
async void is almost always incorrect, except for event handlers and rare fire-and-forget methods, and usually async Task is what you want. Search SO for any number of good answers on why.
Then lastly, what you really should do is profile the code to see where your real bottlenecks are.
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 ();
}
I am new the using Task.Run() along with async and await to make UI more responsive, so likely I have not implemented something correctly.
I have reviewed the great articles from Stephen Cleary about using AsyncCommands and have used his code from Patterns for Asynchronous MVVM Applications: Commands as a basis for having a responsive UI but when I run the code it still seems to freeze up (I am not able to move the window or interact with other buttons until the function has fully finished.
I am trying to perform a search which usually takes 5-10 seconds to return. Below is the code that creates the AsyncCommand along with the what the function does.
Code:
public ICommand SearchCommand
{
get
{
if (_SearchCommand == null)
{
_SearchCommand = AsyncCommand.Create(() => Search());
}
return _SearchCommand;
}
}
private async Task Search()
{
IEnumerable<PIPoint> points = await SearchAsync(_CurrentPIServer, NameSearch, PointSourceSearch).ConfigureAwait(false);
SearchResults.Clear();
foreach (PIPoint point in points)
{
SearchResults.Add(point.Name);
}
}
private async Task<IEnumerable<PIPoint>> SearchAsync(string Server, string NameSearch, string PointSourceSearch)
{
{
PIServers KnownServers = new PIServers();
PIServer server = KnownServers[Server];
server.Connect();
return await Task.Run<IEnumerable<PIPoint>>(()=>PIPoint.FindPIPoints(server, NameSearch, PointSourceSearch)).ConfigureAwait(false);
}
}
I am thinking that the issue is somewhere in how I am pushing the long running function onto a thread and its not getting off of the UI thread or my understanding of how Tasks and async/await are completely off.
EDIT 1:
Following Stephen's answer I updated the functions, but I am not seeing any change in the UI responsiveness. I created a second command that performs the same actions and I get the same response from UI in either case. The code now looks like the following
CODE:
public ICommand SearchCommand
{
get
{
if (_SearchCommand == null)
{
_SearchCommand = AsyncCommand.Create(async () =>
{
var results = await Task.Run(()=>Search(_CurrentPIServer, NameSearch, PointSourceSearch));
SearchResults = new ObservableCollection<string>(results.Select(x => x.Name));
});
}
return _SearchCommand;
}
}
public ICommand SearchCommand2
{
get
{
if (_SearchCommand2 == null)
{
_SearchCommand2 = new RelayCommand(() =>
{
var results = Search(_CurrentPIServer, NameSearch, PointSourceSearch);
SearchResults = new ObservableCollection<string>(results.Select(x => x.Name));
}
,()=> true);
}
return _SearchCommand2;
}
}
private IEnumerable<PIPoint> Search(string Server, string NameSearch, string PointSourceSearch)
{
PIServers KnownServers = new PIServers();
PIServer server = KnownServers[Server];
server.Connect();
return PIPoint.FindPIPoints(server, NameSearch, PointSourceSearch);
}
I must be missing something but I am not sure what at this point.
EDIT 2:
After more investigation on what was taking so long it turns out the iterating of the list after the results are found is what was hanging the process. By simply changing what the Search function was returning and having it already iterated over the list of objects allows for the UI to remain responsive. I marked Stephen's answer as correct as it handled my main problem of properly moving work off of the UI thread I just didnt move the actual time consuming work off.
My first guess is that the work queued to Task.Run is quite fast, and the delay is caused by other code (e.g., PIServer.Connect).
Another thing of note is that you are using ConfigureAwait(false) in Search which updates SearchResults - which I suspect is wrong. If SearchResults is bound to the UI, then you should be in the UI context when updating it, so ConfigureAwait(false) should not be used.
That said, there's a Task.Run principle that's good to keep in mind: push Task.Run as far up your call stack as possible. I explain this in more detail on my blog. The general idea is that Task.Run should be used to invoke synchronous methods; it shouldn't be used in the implementation of an asynchronous method (at least, not one that is intended to be reused).
As a final note, async is functional in nature. So it's more natural to return results than update collections as a side effect.
Combining these recommendations, the resulting code would look like:
private IEnumerable<PIPoint> Search(string Server, string NameSearch, string PointSourceSearch)
{
PIServers KnownServers = new PIServers();
PIServer server = KnownServers[Server];
// TODO: If "Connect" or "FindPIPoints" are naturally asynchronous,
// then this method should be converted back to an asynchronous method.
server.Connect();
return PIPoint.FindPIPoints(server, NameSearch, PointSourceSearch);
}
public ICommand SearchCommand
{
get
{
if (_SearchCommand == null)
{
_SearchCommand = AsyncCommand.Create(async () =>
{
var results = await Task.Run(() =>
Search(_CurrentPIServer, NameSearch, PointSourceSearch));
SearchResults = new ObservableCollection<string>(
results.Select(x => x.Name));
});
}
return _SearchCommand;
}
}
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;
}