What is the C# equivalent to Promise.all? - c#

I would like to fetch data from multiple locations from
Firebase Realtime Database like described here and here by Frank van Puffelen and I can't find any equivalent to Promise.all in c#. What would be the proper way to do it?

That you are looking for is Task.WhenAll. You should create as many tasks as the multiple locations from which you want to fetch your data and then feed them in this method.

To expand on #Christos's accepted answer:
Task.WhenAll appears to be about as close as you will get for a drop-in replacement for Promise.all. I actually found it to be closer than I initially thought. Here's an example using a JavaScript Promise.all implementation that you may want to replicate in C#:
const [ resolvedPromiseOne, resolvedPromiseTwo ] = await Promise.all([ taskOne, taskTwo ]);
In C# you can do something very similar with Task.WhenAll (assuming they return the same types).
var taskList = new[]
{
SomeTask(),
AnotherTask()
};
var completedTasks = await Task.WhenAll(taskList);
// then access them like you would any array
var someTask = completedTasks[0];
var anotherTask = completedTasks[1];
// or just iterate over the array
foreach (var task in completedTasks)
{
doSomething(task);
}
This assumes they're both in async methods / functions.

Related

How to run mutliple tasks at once and add return values to a list?

What is the easiest way to run multiple tasks at once and add their return values to a list in c sharp?
I tried a couple ways but I am probably doing everything wrong therefore I don't have much code to post on here.
UPDATE:
Here is one of my fail tries. I am basically trying to get the JSON dictionary from a link (links will be different for each task) and add all the JSON dictionaries to a single list at the end. But I want all the tasks to run at the same time.
static class Program
{
static void Main()
{
List<dynamic> writeList = new List<dynamic>();
for (int i = 0; i <= 50; i++)
{
Task<dynamic> task = Task<dynamic>.Factory.StartNew(
() => savePageData("www.someJsonlink.com"));
writeList.Add(task);
}
}
static dynamic savePageData(string url)
{
WebClient client = new WebClient();
string page = client.DownloadString(url);
var data = JsonConvert.DeserializeObject(page);
return data;
}
}
The problem with this approach is that nothing gets added to the list. Actually no connection is made to the link either. But when done normally, the data is present.
The easiest way is probably to use the Select LINQ operator, combined with the Task.WhenAll method:
int[] ids = GetIds();
Task<Image>[] tasks = ids.Select(id => DownloadImageAsync(id)).ToArray();
Image[] results = await Task.WhenAll(tasks);
In this example the Select operator projects each int to a Task<Image>, resulting in an IEnumerable<Image> sequence. This sequence is materialized to an array of Task<Image> by using the ToArray LINQ operator.
If the method you are calling for each image is not asynchronous, then instead of .Select(id => DownloadImageAsync(id)) you can do this:
.Select(id => Task.Run(() => DownloadImage(id))). This way multiple DownloadImage will be called in parallel, using ThreadPool threads. The number of threads in the pool is limited, so most likely you won't get the same degree of parallelism with what you would get with the truly asynchronous DownloadImageAsync invocations.

How to get await an array οf tasks (Task<T>[]) to become an array of things (T[])?

This is not a question on how to use async and await in general. It considers a very specific case that I haven't found in the documentation.
I have a setup of types that I'm not used to. Usually, I put the await and the Task<Whatever> gets de-task'ified to the type it's going to have in the end. Now and I got stuck on the following and after a while of trying, I realize I don't know how to get rid of the Task<Thing> type in the output. I want it to be Thing only.
Task<Thing>[] output = list.Select(async a => new Thing
{
Id = a.Id,
Name = a.Name,
Props = (await SomeService.GetAsync(a))
.Select(b => b.Value).ToArray()
}).ToArray();
The requirement is that the declaration of output is explicit (i.e. no var) and that it's not asynchronous (i.e. no Task). The type of output must be Thing[] and I prefer not to use Task.WhenAll(output) as it requires declaring output earlier, which breaks the first two requirements.
The inner await is required in order to be able to apply Select. But that imposes the outer async, hence making it an array of tasks of things (i.e. Task<Thing>[]), instead of an array of things (i.e. Thing[]).
I can't put await in front of the list because an array of tasks isn't awaitable. I can't put it inside the lamba expression becuase it breaks the syntax.
I've made a few searches for syntaxes and examples but this is a bit uncommon scenario so I found little of value. Can even be done? I'm convinced it can but I can't figure our how. I have found only this question. It's related but not sufficiently similar, though. And the answer doesn't actually show how to achieve what I want (which is eliminating var and Task in the variable declaration).
I suspect you're looking for Task.WhenAll:
Thing[] result = await Task.WhenAll(output);
To avoid having to use Task<Thing>[] anywhere, or putting a long expression as an argument for Task.WhenAll, you could write an extension method:
public static class TaskEnumerable
{
public static Task<T[]> WhenAllTasks<T>(this IEnumerable<Task<T>> tasks) =>
Task.WhenAll(tasks);
}
Your code can then be:
Thing[] output = await list.Select(async a => new Thing
{
Id = a.Id,
Name = a.Name,
Props = (await SomeService.GetAsync(a))
.Select(b => b.Value).ToArray()
}).WhenAllTasks();

Mongo C# driver - Contains Filter

I am using the latest version of Mongo C# driver which uses a lot of Async and builder pattern. Which is nice. I am trying to convert SQL where clauses into Mongo FilterDefinition object.
Any idea how to handle "contains"?
like:
where x contains 'ABC'
In order to achieve that in V2 API, use the `Filter.Regex':
var collection = db.GetCollection<BsonDocument>("collection");
var filter = Builders<BsonDocument>.Filter.Regex("fieldName", new BsonRegularExpression(".*fieldValue.*"));
var data = await (await coll.FindAsync<BsonDocument>(filter).ConfigureAwait(false)).ToListAsync();
//continue process data
If x is a string, you could do so with a simple regex. For the 2.0 driver, you can manually create the FilterDefinition:
FilterDefinition<BsonDocument> filter = "{ x : { $regex : /ABC/ } }";
Or build the filter use the Builder:
var builder = Builders<BsonDocument>.Filter;
var filter = builder.Matches("x", "ABC");
Then you can use the filter in your query:
using (var cursor = await collection.Find(filter).ToCursorAsync())
{
// ...
}
I was able to get this working using Filter.AnyIn like so
var filter = Builders<BsonDocument>.Filter.AnyIn("x", new List<string> { "ABC" });
This works if you're looking for multiple values too, just add them to the list.
First, I highly recommend taking MongoDB University's .NET course (from Mongo itself). It's really thorough, and covers your question (and more) in depth.
Second, I assume that x is an array in your example.
MongoDB correctly handles polymorphism with arrays. If you have a class Post with an array of Tags, you can filter where Tag = ABC.
If you're using the C# linq methods, that looks like .Find(p => p.Tags == "ABC"). If you're using BsonDocument, that looks like new BsonDocument().Add("Tags", "ABC").
I have another way which I don't love but it works. The answer that is marked correct is half wrong (Matches is a method of Builders). In this example the / act like a % in a sql query LIKE statement. I'm still looking for a better way and will update if I find one that is more Equals filter below.
List<yourobject> someList = await collection.Find("{ x: /Test/ }").ToListAsync();
var filter = Builders<yourobject>.Filter.Eq("x", "ABC");
List<yourobject> someList = await collection.Find(filter).ToListAsync();
If you want to just search input text you need to replace regex special characters.
Regex.Escape will ensure that these characters are processed literally rather than as metacharacters. Otherwise input text can be used to query regex patterns which is probably not what is required.
var text = "ABC";
var filter = Builders<BsonDocument>.Filter.Regex("x", BsonRegularExpression.Create(Regex.Escape(text)));
If you need case insensitive check. Then you can pass case insensitive regex to BsonRegularExpression.Create:
var text = "ABC";
var escapeText = Regex.Escape(text);
var regex = new Regex(escapeText, RegexOptions.IgnoreCase);
var filter = Builders<BsonDocument>.Filter.Regex("x", BsonRegularExpression.Create(regex));

Get All 'documents' from MongoDB 'collection'

I need to retrieve all the documents that are in my collection in MongoDB, but I cannot figure out how. I have declared my 'collection' like this-
private static IMongoCollection<Project> SpeCollection = db.GetCollection<Project>("collection_Project");
And I followed what is explained in this MongoDB tutorial. I adjusted it for my needs, like-
var documents = await SpeCollection.Find(new Project()).ToListAsync();
However, I keep having the following error-
MongoDB.Driver.IMongoCollection does not have a definition for 'Find' and the best override of the extension method [superlong stuff]. Find contains non valid arguments.
Using the current version of the driver (v2.0) you can do that by passing a filter that matches everything:
var documents = await SpeCollection.Find(_ => true).ToListAsync();
They have also added an empty filter (FilterDefinition.Empty) which will arrive in the next version of the driver (v2.1):
var documents = await SpeCollection.Find(Builders<Project>.Filter.Empty).ToListAsync();
Simplest Way
Retrieve all the documents-
var documents = SpeCollection.AsQueryable();
Also convert to JSON object-
var json = Json(documents, JsonRequestBehavior.AllowGet);
If you want all documents, why not use Find all?
var documents = await SpeCollection.Find(new BsonDocument()).ToListAsync();

Merge two JSON objects programmatically

I have two JSON objects here, generated through the Google Search API. The URL's of these objects can be found below.
http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=hello%20world&rsz=large
http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=hello%20world&rsz=large&start=8
As you can see the first URL returns the first eight results, whilst the second one returns the next eight. Instead of checking these results separately I'd like to programmatically merge them into one JSON object and pass them through as the first sixteen results.
I've attempted this with a couple of extremely simple JSON objects, but what Google returns is still a bit above my head, so I'm hoping for a bit of help with doing such a thing.
As far as I've been told it is not against Google's Terms of Service to merge two objects into one, only that these always go through as two results (which they will). Some friends have pointed me in the direction of automated tools that are capable of doing such things, but I'm yet to find such a tool.
I'm currently working within ASP.NET so C# or VB.NET code is great, but I'm somewhat language independent so any help in any language will be very much appreciated.
Can anyone provide any help and/or advice on doing such a thing?
EDIT: These results will eventually be saved to a database, so any server-side methods would be fantastic, even if it means putting them straight into a table for dealing with later.
function MergeJSON (o, ob) {
for (var z in ob) {
o[z] = ob[z];
}
return o;
}
This looks a lot like the code from Elliot, but is a bit safer in some conditions. It is not adding a function to the object, which could lead to some syntax problems, when used in with a framework like Extjs or jQuery. I had the problem that it gave me problems in the syntax when used in an event listener. But credits go to Elliot, he did the job.
Use this as following:
a = {a : 1}
b = {b : 2}
c = {c : 3}
x = MergeJSON ( a, b);
x = MergeJSON ( x, c);
result : x == {a : 1, b : 2, c : 3}
Thank you Elliot
Object.prototype.merge = (function (ob) {
var o = this;
var i = 0;
for (var z in ob) {
if (ob.hasOwnProperty(z)) {
o[z] = ob[z];
}
}
return o;
})
var a = {a:1}
var b = {b:2}
var c = a.merge(b); // === {a:1,b:2}
Rather than merge the two results together, I just decided to parse them, then link those two together. In the end there was really no need to merge the two together when they could be easily joined within a database.
If you are up to a client side solution(JavaScript actually) what about trying the "unite" function I have written: http://code.google.com/p/av-jslib/source/browse/js/aV.ext.object.js#36
With Jquery you could do this!
a = $.extend({a:1}, {b:2});
result: Object { a=1, b=2}
http://api.jquery.com/jQuery.extend/
Also, if you really want to do the results manipulation server-sided, this article seems to give a pretty reasonable walkthrough of the process.
I'm not sure how you'd merge these things completely, given that there's a lot of extra data in each apart from the results themselves, but if you just want a JavaScript array containing all 16 results, this should work...
var responses = [GetJsonObjectFromThatUriUsingJqueryOrSomething(),
GetJsonObjectFromThatOtherUriUsingJqueryOrSomething()];
// Probably want to check for success
// and ensure that responses[i].responseData.results is valid.
var results = [];
for (var i = 0; i < responses.length; ++i)
{
results = results.concat(responses[i].responseData.results);
}
To make it more neat, you can add the merge function to the JSON object.
This is the solution from Johan van de Merwe based on Elliot's answer, added to the actual JSON object.
// Extend JSON object
JSON.merge = function (o,ob) {
for (var z in ob) {
o[z] = ob[z];
}
return o;
}
json3 = JSON.merge(json1,json2);

Categories