I am using Firebase real-time database for my app build with Unity. In order to build a "friend" leaderboard, the database will keep track of users their friends and their scores.
The database has the following structure:
scores{
user id : score
}
Users:{
Id: {
ageRange
email
name
friendlist : {
multiple friends user ids
}
}
}
The problem is in order to get the scores and the names of every friend the app has to make alot of api calls. Atleast if I correctly understand firebase. If the user has 10 friends it will take 21 calls before the leaderboard is filled.
I came up with the following code written in c#:
List<UserScore> leaderBoard = new List<UserScore>();
db.Child("users").Child(uid).Child("friendList").GetValueAsync().ContinueWith(task => {
if (task.IsCompleted)
{
//foreach friend
foreach(DataSnapshot h in task.Result.Children)
{
string curName ="";
int curScore = 0;
//get his name in the user table
db.Child("users").Child(h.Key).GetValueAsync().ContinueWith(t => {
if (t.IsCompleted)
{
DataSnapshot s = t.Result;
curName = s.Child("name").Value.ToString();
//get his score from the scores table
db.Child("scores").Child(h.Key).GetValueAsync().ContinueWith(q => {
if (q.IsCompleted)
{
DataSnapshot b = q.Result;
curScore = int.Parse(b.Value.ToString());
//make new userscore and add to leaderboard
leaderBoard.Add(new UserScore(curName, curScore));
Debug.Log(curName);
Debug.Log(curScore.ToString());
}
});
}
});
}
}
});
Is there any other way to do this? I've read multiple stack overflow questions watched firebase tutorials but i didnt found any simpler or more efficient way to get the job done.
There's not a way of reducing the number of API calls without duplicating data/restructuring your database. However, reducing API calls doesn't necessarily mean the overall read strategy is faster/better. My suggestion would be to optimize your reading strategy to reduce overall data read and to make reads concurrently when possible.
Solution 1: Optimize reading strategy
This is my recommended solution, because it doesn't include extra unnecessary data nor does it include managing consistency of your data.
Your code should look something like below:
(DISCLAIMER: I'm not a C# programmer, so there might be some errors)
List<UserScore> leaderBoard = new List<UserScore>();
db.Child("users").Child(uid).Child("friendList").GetValueAsync().ContinueWith(task => {
if (task.IsCompleted)
{
//foreach friend
foreach(DataSnapshot h in task.Result.Children)
{
// kick off the task to retrieve friend's name
Task nameTask = db.Child("users").Child(h.Key).Child("name").GetValueAsync();
// kick off the task to retrieve friend's score
Task scoreTask = db.Child("scores").Child(h.Key).GetValueAsync();
// join tasks into one final task
Task finalTask = Task.Factory.ContinueWhenAll((new[] {nameTask, scoreTask}), tasks => {
if (nameTask.IsCompleted && scoreTask.IsCompleted) {
// both tasks are complete; add new record to leaderboard
string name = nameTask.Result.Value.ToString();
int score = int.Parse(scoreTask.Result.Value.ToString());
leaderBoard.Add(new UserScore(name, score));
Debug.Log(name);
Debug.Log(score.ToString());
}
})
}
}
});
The above code improves the overall read strategy by not pulling all of a friend's user data (i.e. name, email, friendlist, etc.) and by pulling the name concurrently with score.
Solution 2: Duplicate name to scores table
If this still isn't optimal enough, you can always duplicate the friend's name in their score table. Something like below:
scores: {
<user_id>: {
name: <user_name>,
score: <user_score>
}
}
This would then allow you to only make one call per friend instead of two. However, you will still be reading the same amount of data, and you will have to manage the consistency of the data (either use a Firebase Function to propagate user name changes or write to both places).
Solution 3: Combine scores table into users table
If you don't want to manage the consistency issue, you can just combine the scores table into the users table.
Your structure would be something like:
users: {
<user_id>: {
name: <user_name>,
...,
score: <user_score>
}
}
However, in this instance, you will be reading more data (email, friendlist, etc.)
I hope this helps.
While the following will not reduce the number of calls, it will create tasks for the retrieval of the data and run them all simultaneously, returning the list of desired user scores.
var friendList = await db.Child("users").Child(uid).Child("friendList").GetValueAsync();
List<Task<UserScore>> tasks = new List<Task<UserScore>>();
//foreach friend
foreach(DataSnapshot friend in friendList.Children) {
var task = Task.Run( async () => {
var friendKey = friend.Key;
//get his name in the user table
var getName = db.Child("users").Child(friendKey).Child("name").GetValueAsync();
//get his score from the scores table
var getScore = db.Child("scores").Child(friendKey).GetValueAsync();
await Task.WhenAll(getName, getScore);
var name = getName.Result.Value.ToString();
var score = int.Parse(getScore.Result.Value.ToString());
//make new userscore to add to leader board
var userScore = new UserScore(name, score);
Debug.Log($"{name} : {score}");
return userScore;
});
tasks.Add(task);
}
var scores = await Task.WhenAll(tasks);
List<UserScore> leaderBoard = new List<UserScore>(scores);
This is mostly database structure issue.
First, you need leaderboard table.
leaderboard: {
<user_id>: <score>
}
Second, you need users table.
users: {
<user_id>: {
name: <user_name>,
...,
score: <user_score>,
friendlist: {
multiple friends user ids
}
}
}
And you have to update leaderboard's score and users' score at the same time.
If you want to avoid Callback hell.
You can also try like this. (This code is JAVA)
// Create a new ThreadPoolExecutor with 2 threads for each processor on the
// device and a 60 second keep-alive time.
int numCores = Runtime.getRuntime().availableProcessors();
ExecutorService executor = new ThreadPoolExecutor(
numCores * 2,
numCores * 2,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>()
);
Tasks.call(executor, (Callable<Void>) () -> {
Task<Token> getStableTokenTask = NotificationUtil.getStableToken(token);
Token stableToken;
stableToken = Tasks.await(getStableTokenTask);
if (stableToken != null) {
Task<Void> updateStableTokenTask = NotificationUtil.updateStableToken(stableToken.getId(), versionCode, versionName);
Tasks.await(updateStableTokenTask);
}
if (stableToken == null) {
Token newToken = new Token(token, versionCode, versionName);
Task<Void> insertStableTokenTask = NotificationUtil.insertStableToken(newToken);
Tasks.await(insertStableTokenTask);
}
return null;
}).continueWith((Continuation<Void, Void>) task -> {
if (!task.isSuccessful()) {
// You will catch every exceptions from here.
Log.w(TAG, task.getException());
return null;
}
return null;
});
Related
I have an Azure Function that is triggered by eventhub and sends data in batches. Inside function, there are multiple calls to insert data into CosmosDB. I have added following code as part of App Insight Monitoring.
builder.Services.AddApplicationInsightsTelemetry();
builder.Services.ConfigureTelemetryModule<DependencyTrackingTelemetryModule>((module, o) =>
{
module.EnableW3CHeadersInjection = true;
});
builder.Services.ConfigureTelemetryModule<EventCounterCollectionModule>(
(module, o) =>
{
module.Counters.Add(new EventCounterCollectionRequest("System.Runtime", "gen-0-size"));
}
);
I can see total response time in App Insight but cannot figure out how to track and send time spent by each insert query in CosmosDB
Here is C# code within Azure Function
var watch = System.Diagnostics.Stopwatch.StartNew();
var DemoContainerData = new
{
id = Guid.NewGuid().ToString(),
UserId = userId,
// other properties
};
_demoContainer.CreateItemAsync<object>(DemoContainerData);
var DemoContainerData2 = new
{
id = Guid.NewGuid().ToString(),
ProductId = productId,
// other properties
};
_productContainer.CreateItemAsync<object>(DemoContainerData2);
/* var dependency = new DependencyTelemetry
{
Name = "",
Target = "",
Data = ",
Timestamp = start,
Duration = DateTime.UtcNow - start,
Success = true
};
this._telemetryClient.TrackDependency(dependency);
*/
watch.Stop();
var elapsed = watch.Elapsed.TotalMilliseconds;
log.LogInformation("Total Items {0} - Total Time {1}", Items.Length, elapsed);
Your code is not awaiting the async operations, you should be:
ItemResponse<object> response = await _demoContainer.CreateItemAsync<object>(DemoContainerData);
From the response, you can measure the client latency:
var elapsedTimeForOperation = response.Diagnostics.GetClientElapsedTime();
What we recommend if you want to investigate high latency is to log the Diagnostics when the request goes above some threshold, for example:
if (response.Diagnostics.GetClientElapsedTime() > ConfigurableSlowRequestTimeSpan)
{
// Log the diagnostics and add any additional info necessary to correlate to other logs
log.LogWarning("Slow request {0}", response.Diagnostics);
}
For best latency, make sure you are following https://learn.microsoft.com/azure/cosmos-db/sql/troubleshoot-dot-net-sdk-slow-request?tabs=cpu-new#application-design (mainly make sure you are using a Singleton client, use ApplicationRegion or ApplicationPreferredRegions to define preferred region to connect which hopefully is the same region the Function is deployed to).
If I query items from CosmosDB with SkipToken,
Like Pseudo code:
do{
var page = Query();
foreach(var item in page)
{
Update(item);
}
}while(HasNextPage());
The page I get may not be complete, which means I will miss some item.
But if I wait a moment after Update
Like:
do{
var page = Query();
foreach(var item in page)
{
Update(item);
}
// difference here:
WaitAMoment();
}while(HasNextPage());
, the error will not happen, and I will get the complete page with all I need.
So what happened to such a process?
You don't have to wait in code as such, this functionality is handled by CosmosDB internally. Check out Pagination in SDKs of Cosmos DB and, for example sake, I am adding code of handling server-side pagination in C# below (to get a gist of how it works):
private static async Task QueryPartitionedContainerInParallelAsync(Container container)
{
List<Family> familiesSerial = new List<Family>();
string queryText = "SELECT * FROM Families";
// 0 maximum parallel tasks, effectively serial execution
QueryRequestOptions options = new QueryRequestOptions() { MaxBufferedItemCount = 100 };
options.MaxConcurrency = 0;
using (FeedIterator<Family> query = container.GetItemQueryIterator<Family>(
queryText,
requestOptions: options))
{
while (query.HasMoreResults)
{
foreach (Family family in await query.ReadNextAsync())
{
familiesSerial.Add(family);
}
}
}
Assert("Parallel Query expected two families", familiesSerial.ToList().Count == 2);
// 1 maximum parallel tasks, 1 dedicated asynchronous task to continuously make REST calls
List<Family> familiesParallel1 = new List<Family>();
options.MaxConcurrency = 1;
using (FeedIterator<Family> query = container.GetItemQueryIterator<Family>(
queryText,
requestOptions: options))
{
while (query.HasMoreResults)
{
foreach (Family family in await query.ReadNextAsync())
{
familiesParallel1.Add(family);
}
}
}
Assert("Parallel Query expected two families", familiesParallel1.ToList().Count == 2);
AssertSequenceEqual("Parallel query returns result out of order compared to serial execution", familiesSerial, familiesParallel1);
// 10 maximum parallel tasks, a maximum of 10 dedicated asynchronous tasks to continuously make REST calls
List<Family> familiesParallel10 = new List<Family>();
options.MaxConcurrency = 10;
using (FeedIterator<Family> query = container.GetItemQueryIterator<Family>(
queryText,
requestOptions: options))
{
while (query.HasMoreResults)
{
foreach (Family family in await query.ReadNextAsync())
{
familiesParallel10.Add(family);
}
}
}
Assert("Parallel Query expected two families", familiesParallel10.ToList().Count == 2);
AssertSequenceEqual("Parallel query returns result out of order compared to serial execution", familiesSerial, familiesParallel10);
}
We're experimenting with the MS Bot Framework and haven't quite worked out how to do this scenario:
We have a LUIS Dialog (type <object>), which is working correctly and is trained properly. To use the common sandwich example, the basics of what LUIS intent is looking for is the user asking for the status of an order. If the order number was provided in the question ("What is the status of order 1234?"), then the LUIS dialog does the lookup and reports the status directly (which is all currently working).
However, if the user just triggers the intent without providing the order number ("I'd like to look up the status of an order."), I'd like to launch another dialog/form to ask the user if they'd like to look up the order by address or order number, and then do the appropriate DB lookup based on how they answer.
I'm just not sure how to configure the Form/Dialog (or even which is best in this case) to do a different lookup based on if they choose address or number lookup.
Here's the intent so far:
private readonly BuildFormDelegate<OrderStatusDialog> OrderStatusDelegate;
[LuisIntent(nameof(LuisIntents.OrderStatus))]
public async Task OrderStatus(IDialogContext context, LuisResult result)
{
// Order number(s) were provided
if (result.Entities.Any(Entity => Entity.Type == nameof(LuisEntityTypes.OrderNumber)))
{
// Loop in case they asked about multiple orders
foreach (var entity in result.Entities.Where(Entity => Entity.Type == nameof(LuisEntityTypes.OrderNumber)))
{
var orderNum = entity.Entity;
// Call webservice to check status
var request = new RestRequest(Properties.Settings.Default.GetOrderByNum, Method.GET);
request.AddUrlSegment("num", orderNum);
var response = await RestHelper.SendRestRequestAsync(request);
var parsedResponse = JObject.Parse(response);
if ((bool)parsedResponse["errored"])
{
await context.PostAsync((string)parsedResponse["errMsg"]);
continue;
}
// Grab status from returned JSON
var status = parsedResponse["orderStatus"].ToString();
await context.PostAsync($"The status of order {orderNum} is {status}");
}
context.Wait(MessageReceived);
}
// Order number was not provided
else
{
var orderStatusForm = new FormDialog<OrderStatusDialog>(new OrderStatusDialog(), OrderStatusDelegate,
FormOptions.PromptInStart);
context.Call<OrderStatusDialog>(orderStatusForm, CallBack);
}
}
private async Task CallBack(IDialogContext context, IAwaitable<object> result)
{
context.Wait(MessageReceived);
}
And the form:
public enum OrderStatusLookupOptions
{
Address,
OrderNumber
}
[Serializable]
public class OrderStatusDialog
{
public OrderStatusLookupOptions? LookupOption;
public static IForm<OrderStatusDialog> BuildForm()
{
return new FormBuilder<OrderStatusDialog>()
.Message("In order to look up the status of a order, we will first need either the order number or your delivery address.")
.Build();
}
}
The FormFlow route is a valid option. What is missing in your form flow is asking for the address/order number after the lookup option is selected.
What you can do in that case is adding two more fields to the OrderStatusDialog class: OrderNumber and DeliveryAddress.
Then you need to use the selected OrderStatusLookupOptions to activate/deactivate the next field.
The code, from the top of my head, would be something like:
[Serializable]
public class OrderStatusDialog
{
public OrderStatusLookupOptions? LookupOption;
public int OrderNumber;
public string DeliveryAddress
public static IForm<OrderStatusDialog> BuildForm()
{
return new FormBuilder<OrderStatusDialog>()
.Message("In order to look up the status of a order, we will first need either the order number or your delivery address.")
.Field(nameof(OrderStatusDialog.LookupOption))
.Field(new FieldReflector<OrderStatusDialog>(nameof(OrderStatusDialog.OrderNumber))
.SetActive(state => state.LookupOption == OrderStatusLookupOptions.OrderNumber))
.Field(new FieldReflector<OrderStatusDialog>(nameof(OrderStatusDialog.DeliveryAddress))
.SetActive(state => state.LookupOption == OrderStatusLookupOptions.Address))
.Build();
}
}
Then on your Callback method you will receive the form filled and you can do the DB lookup.
Alternatively, you can just use PromptDialogs and guide the user through the same experience. Take a look to the MultiDialogs sample to see the different alternatives.
I added a working sample on this here.
So the problem is as follows: I'm using a third party client class to issue commands to an external system to retrieve data (fairly standard). The problem is that when I issue commands via an instance of this class, it uses a callback reference based on the function name in my code and the line number within that function from which it was called, it then serializes this and other information into JSON and transmits to the external system for processing, with the data return being identified by the reference. This works "well" until we get to iteration, then the callback reference remains the same and I only receive data for one iteration. The third party isn't going to alter their code so I need a way of generating unique references in my code, but I'm unsure of how I can do this within C#. I can't edit their class as it is provided as a DLL, I can only access this system by using it (it is part of their SDK).
Any ideas greatly appreciated!
Example code:
[Note: actual code is part of a Windows Form Application]
The last part client.IsUserInGroup is the problem.
using thirdParty.Client;
class TestProgram
{
static void Main(string[] args)
{
//Area and user objects defined within third party class
List<Area> Areas = new List<Area>();
List<User> myUsers = new List<User>();
int publicAreaID = 0;
bool isConnected=false;
client.Connect("user", "pass",
(connstatus) =>
{
switch (connstatus)
{
case ConnectionStatus.Success:
isConnected = true;
Console.WriteLine("Connected");
break;
case ConnectionStatus.InvalidCredentials:
Console.WriteLine("InvalidCredentials");
break;
case ConnectionStatus.Timeout:
Console.WriteLine("Timeout");
break;
}
});
if (isConnected)
{
client.GetAreas(
(result) =>
{
Areas = result;
});
//Get ID of public area
foreach (Area myArea in Areas)
{
if (myArea.Name.Equals("Public"))
{
publicAreaID = myArea.ID;
}
}
//Get all keyholders in Public area and store in list
client.GetUsersInArea(publicAreaID,
(result) =>
{
myUsers = result;
});
//Iterate over all users in list and verify they are in the everyone group
foreach (User myUser in myUsers)
{
User tempUser = myUser;
client.IsUserInGroup(tempUser.ID, 0,
(result) =>
{
if (result) //this is a bool
{
//This only returns one result..
Console.WriteLine(String.Format("{0} is in Everyone Group and Public Area", tempUser.Name));
}
});
}
client.Disconnect();
}
}
}
UPDATE
I've been doing more testing by removing the foreach loop and just calling client.IsUserInGroup twice to generate alternative callback references; the results are interesting. As expected there are unique references, but there is still only one result displayed, the only way to get both is to create two User objects rather than reuse one. As mentioned above, the "real" code is used in a Windows forms app, could this be something to do with object referencing? Example code below:
new User tempUser1 = myUsers[0];
client.IsUserInGroup(tempUser1.ID, 0,
(result) =>
{
if (result) //this is a bool
{
Console.WriteLine(String.Format("{0} is in Everyone Group and Public Area", tempUser1.Name));
}
});
new User tempUser2 = myUsers[1];
client.IsUserInGroup(tempUser2.ID, 0,
(result) =>
{
if (result) //this is a bool
{
Console.WriteLine(String.Format("{0} is in Everyone Group and Public Area", tempUser2.Name));
}
});
Answer moved from OP's original question:
Ok so I was playing with this a lot over the last few hours and kind of made it work by keeping the iterative loop but doing two things; firstly I assumed that the third party class would synchronize information requests and not allow my code to continue until it had a result returned - this seemingly is not the case as the output from recursion with an extra Console.WriteLine(iterationCount) in it shows the count increasing with no data returned; therefore I am forced to slow down the code by Thread.Sleep (I'm investigating better ways of doing this). Secondly any code within the lambda that could be moved outside, was. Instead a temp bool outside of the lambda was assigned the value of the result bool. The code looks like:
//Iterate over all users in list and verify they are in the everyone group
foreach (User myUser in myUsers)
{
User tempUser = myUser;
bool tempRes = false;
client.IsUserInGroup(tempUser.ID, 0,
(result) =>
{
tempRes = result;
});
if (tempRes)
{
Console.WriteLine(String.Format("{0} is in Everyone Group and Public Area", tempUser.Name));
}
System.Threading.Thread.Sleep(75); //Not a good way of enforcing sync!
}
I am not sure what to use in this scenario.
I have an asp.net web api method that basically does this
Finds points of interests from foursquare near user.
Uses the foursquare locations to do queries in my database to find unique data about point of interest near user.
However since I need to store some of the foursquare information to link to my unique data to that location I decided to store all the information in my database and have my database act as my caching system.
This means anything new point of interest that comes in I have to insert into my database, check if it exists and if so then skip it or if it exists check the last refresh date(foursquare policy states all data must be refreshed after 30 day) and if it out past the refresh date I have to update the data.
I want to slow the user down and have to wait for the above to happen. I want my code to do step 1 and then do what I just mentioned while at the same time doing step 2.
Once step 2 finishes I want to return the data and let the user get on their way. If my caching system is not finished then it should keep going but not bog down the user.
I won't use any of these new results in step 2 as if I am inserting it then I won't have any data on that location at this time.
Not sure if I need to make a thread or use the async/await to achieve this.
Edit
Here is what I am trying to do
public HttpResponseMessage Get()
{
// this will do a foursquare lookup to find all stores near the user
// I want to insert them into my database and link it to my unquie data.
// stores pulled from foursquare will
// a) Be new and not in my database
// b) exist in my database but have been refreshed lately
// c) have not been refreshed in timeframe of foursquare policy
// THIS SHOULD WORK IN THE BACKGROUND
storeService.PointsOfInterestNearUser(80, -130); //As you can see it is
//void. Not sure where to put the async/await stuff
// find this product. Should be happening at the same time as above line.
var product = productService.FindProduct("Noodles");
//This will get returned to the user.
// the new stores taht are being added in StoreNearUser
//won't effect this search as I will have not data on this new store
// if existing store is being refreshed it is possible old
//address might be picked up...
//I can live with that as I doubt the address will change much.
// this should happen after product
var allStores = storeService.FindStoresThatHaveItem(product);
// this should be returned as soon as above line is finished.
//If StoreNearUser is not done, it should keep going but not hold up user.
return allStores;
}
public void StoresNearUser(double latitude, double longitude)
{
// get all categories I can about in foursquare.
//First time from db otherwise cached.
List<StoreCategory> storeCategories = GetStoreCategories();
// do a request and get everything in near the user
//(provided it is also in a category I care about)
var request = CreateFoursquareStoreRequest
(latitude, longitude, storeCategories);
// do the actual call.
var response = client.Execute<VenueSearch>(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
// start going through the results, add or update or skip of entry will happen
AddUpdateStores(storeCategories, response);
}
else
{
ErrorSignal.FromCurrentContext().Raise(response.ErrorException);
}
}
Edit 2
public async Task StoresNearUser(double latitude, double longitude)
{
// get all categories I can about in foursquare. First time from db otherwise cached.
List<StoreCategory> storeCategories = GetStoreCategories();
// do a request and get everything in near the user(provided it is also in a category I care about)
var request = CreateFoursquareStoreRequest(latitude, longitude, storeCategories);
await client.ExecuteAsync<VenueSearch>
( request
, response =>
{
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
AddUpdateStores(storeCategories, response);
}
else
{
ErrorSignal.FromCurrentContext()
.Raise(response.ErrorException);
}
}
);
}
gives me this error
Cannot await 'RestSharp.RestRequestAsyncHandle'
I also don't get the difference between Task and void. From what I read if you just use Task it means you are sending nothing back of meaning, then why not just use void?
Edit 2
I found this post to show me how to make the wrapper for Restsharp. It is not 100% what I want but that is a separate issue.
public async Task StoresNearUser(double latitude, double longitude)
{
List<StoreCategory> storeCategories = GetStoreCategories();
var request = CreateFoursquareStoreRequest
(latitude, longitude, maxRadius, returnLimit, storeCategories);
var response = await client.GetResponseAsync(request);
if (response.StatusCode == HttpStatusCode.OK)
{
// had to use json.net right now as the wrapper does not expose restsharps deserilizer
var venue = JsonConvert
.DeserializeObject<VenueSearch>(response.Content);
AddUpdateStores(storeCategories, venue);
}
else
{
ErrorSignal.FromCurrentContext()
.Raise(response.ErrorException);
}
}
public async Task<HttpResponseMessage>Get()
{
await storeService.PointsOfInterestNearUser(80, -130);
var product = productService.FindProduct("Noodles");
var allStores = storeService.FindStoresThatHaveItem(product);
return allStores;
}
When I watch from the debugger it looks like it is still all going in order. I think product and allStores need to be since I need the product before I can find the stores but PointsOfInterestNearUser should be going at the same time as FindProduct.
Edit 3
Here is my FindProduct Method. Not sure what to make async to me it looks like everything needs to wait.
public ResponseResult<Product> FindProduct(string barcode)
{
ResponseResult<Product> responseResult = new ResponseResult<Product>();
Product product = null;
try
{
var findBarCode = context.Barcodes.Where(x => x.Code == barcode).Select(x => x.Product).FirstOrDefault();
responseResult.Response = product;
if (product == null)
{
responseResult.Status.Code = HttpStatusCode.NotFound;
}
else
{
responseResult.Status.Code = HttpStatusCode.OK;
}
}
catch (SqlException ex)
{
ErrorSignal.FromCurrentContext().Raise(ex);
responseResult.Status.Code = HttpStatusCode.InternalServerError;
responseResult.Status.Message = GenericErrors.InternalError;
}
return responseResult;
}
Edit 4
Still not sure how to do the Task.WhenAll()
public async Task<HttpResponseMessage>Get()
{
Task[] tasks = new Task[2];
tasks[0] = storeService.PointsOfInterestNearUser(80, -130);
tasks[1] = productService.FindProduct("Noodles");
await Task.WhenAll(tasks);
// not sure how to get product back out. I looked in the debugger and saw a "Result" that has it but when I do tasks[1].Result inetllisene cannot find .Result
var allStores = storeService.FindStoresThatHaveItem(product);
return allStores;
}
I would recommend using async/await for this. Updating a cache is one of the rare situations where returning early from an ASP.NET request is acceptable. You can see my blog post on the subject for some helpful code.
So, something like this (simplified to just look up one "interesting place" per location):
public async Task<PlaceWithData> FindPlaceAsync(Location myLocation)
{
Place place = await GetPlaceFromFoursquareAsync(myLocation);
PlaceWithData ret = await GetExtraDataFromDatabaseAsync(place);
if (ret.NeedsRefresh)
BackgroundTaskManager.Run(() => UpdateDatabaseAsync(place, ret));
return ret;
}
You may also want to consider extending the ASP.NET caching system rather than doing a "roll your own" cache.