C# Best way to iterate a tree structure of links - c#

I'm trying to gather a list of website links which starting from the root directory can branch down to many sub-directory links, below is a link to a simplified graphic which illustrates the structure, I'm only concerned with getting links in Green, Yellow links always lead to other links, so my output array would contain A,B,D,F,G,H,I. I'm trying to code this in C#.

In generic terms, you can do something like
private static IEnumerable<T> Leaves<T>(T root, Func<T, IEnumerable<T>> childSource)
{
var children = childSource(root).ToList();
if (!children.Any()) {
yield return root;
yield break;
}
foreach (var descendant in children.SelectMany(child => Leaves(child, childSource)))
{
yield return descendant;
}
}
Here, childSource is assumed to be a function that can take an element and return that element's children. In your case, you'll want to make a function that uses something like HtmlAgilityPack to take a given url, download it, and return links from that.
private static string Get(int msBetweenRequests, string url)
{
try
{
var webRequest = WebRequest.CreateHttp(url);
using (var webResponse = webRequest.GetResponse())
using (var responseStream = webResponse.GetResponseStream())
using (var responseStreamReader = new StreamReader(responseStream, System.Text.Encoding.UTF8))
{
var result = responseStreamReader.ReadToEnd();
return result;
}
}
catch
{
return null; // really nothing sensible to do here
}
finally
{
// let's be nice to the server we're crawling
System.Threading.Thread.Sleep(msBetweenRequests);
}
}
private static IEnumerable<string> ScrapeForLinks(string url)
{
var noResults = Enumerable.Empty<string>();
var html = Get(1000, url);
if (string.IsNullOrWhiteSpace(html)) return noResults;
var d = new HtmlAgilityPack.HtmlDocument();
d.LoadHtml(html);
var links = d.DocumentNode.SelectNodes("//a[#href]");
return links == null ? noResults :
links.Select(
link =>
link
.Attributes
.Where(a => a.Name.ToLower() == "href")
.Select(a => a.Value)
.First()
)
.Select(linkUrl => FixRelativePaths(url, linkUrl))
;
}
private static string FixRelativePaths(string baseUrl, string relativeUrl)
{
var combined = new Uri(new Uri(baseUrl), relativeUrl);
return combined.ToString();
}
Note that, in a naive approach, you'll run into an infinite loop if there are any cycles in the links between these pages. To alleviate this, you'll want to avoid expanding the children of a url you've visited before.
private static Func<string, IEnumerable<string>> DontVisitMoreThanOnce(Func<string, IEnumerable<string>> naiveChildSource)
{
var alreadyVisited = new HashSet<string>();
return s =>
{
var children = naiveChildSource(s).Select(RemoveTrailingSlash).ToList();
var filteredChildren = children.Where(c => !alreadyVisited.Contains(c)).ToList();
alreadyVisited.UnionWith(children);
return filteredChildren;
};
}
private static string RemoveTrailingSlash(string url)
{
return url.TrimEnd(new[] {'/'});
}
In case you'd like to prevent your crawler from escaping onto the internet and spending time on Youtube, you'll want
private static Func<string, IEnumerable<string>> DontLeaveTheDomain(
string domain,
Func<string, IEnumerable<string>> wanderer)
{
return u => wanderer(u).Where(l => l.StartsWith(domain));
}
Once you've defined these things, what you want is just
var results = Leaves(
myUrl,
DontLeaveTheDomain(
myDomain,
DontVisitMoreThanOnce(ScrapeForLinks)))
.Distinct()
.ToList();

Related

Performing similar operations on multiple objects with only a single call difference?

I'm attempting to refactor some code written by a previous developer, and I'm scraping my brain trying to come up with a good way to handle this specific use case. We have a method called ProcessMessage that has a switch statement to handle some basic string replacement, but each switch case has a specific object type it's fetching, and a specific method it's calling. The rest of the operations are pretty similar.
First and foremost, I simplified the switch, but I think there may be a way to simplify it further. I was thinking using generics might be possible, but unfortunately these objects are Entities generated by a Database-First Entity Framework context, so I can't do much to modify their underlying types or any interfaces they may inherit.
Here's an example of the switch statement and a couple of the helper methods:
private string ProcessMessage(int? userId, int? resourceId, int? reminderId, string processedMessage)
{
var sourceEntityList = _replacementKeywords.Select(x => x.SourceEntity).Distinct().ToList();
foreach (var sourceEntity in sourceEntityList)
{
var sourcekeyWords = _replacementKeywords.Where(x => x.SourceEntity == sourceEntity).Select(x => x.ReplacementText).ToList();
switch (sourceEntity)
{
case "User":
if (userId.HasValue)
{
var user = _userRepository.GetById(userId);
processedMessage = GetReplacementValue(user, sourcekeyWords, processedMessage);
}
break;
case "Reminder":
if (reminderId.HasValue)
{
var ar = _reminderRepository.GetById(reminderId.Value);
processedMessage = GetReplacementValue(ar, sourcekeyWords, processedMessage);
}
break;
case "Resource":
if (resourceId.HasValue)
{
var scheduleResource = _schedulingRepository.GetresourceIdById(resourceId.Value);
processedMessage = GetReplacementValue(scheduleResource, sourcekeyWords, processedMessage);
}
break;
}
}
return processedMessage;
}
private static string GetReplacementValue(User user, IEnumerable<string> sourceKeywords, string message)
{
var processedMessage = message;
foreach (var userKeyword in sourceKeywords)
{
if (!processedMessage.Contains(userKeyword)) continue;
var replaceValue = GetPatientInfoFromUser(user, userKeyword);
processedMessage = processedMessage.Replace(userKeyword, replaceValue);
}
return processedMessage;
}
private static string GetReplacementValue(Reminder reminder,
IEnumerable<string> sourceKeywords, string message)
{
var processedMessage = message;
foreach (var apptReminder in sourceKeywords)
{
if (!processedMessage.Contains(apptReminder)) continue;
var replaceValue = GetReminderInfo(reminder, apptReminder);
processedMessage = processedMessage.Replace(apptReminder, replaceValue);
}
return processedMessage;
}
private static string GetReplacementValue(Resource resource,
IEnumerable<string> sourceKeywords, string message)
{
var processedMessage = message;
foreach (var resourceKeyword in sourceKeywords)
{
if (!processedMessage.Contains(scheduleResourceKeyword)) continue;
var replaceValue = GetResourceInfo(resource, resourceKeyword);
processedMessage = processedMessage.Replace(resourceKeyword, replaceValue);
}
return processedMessage;
}
The main thing you'll notice is that each of the three helper methods is effectively performing the exact same function with the exception of which method they call to get the replaceValue. Is there a good way to combine these three methods into a single call without touching the underlying entities?
If you think about it, each GetReplacementValue has two responsibilities:
Enumerate through sourceKeywords to process message
Call some function to get the value to replace message's data
Let's take out the second responsibility from there and make it reusable all at once:
private static string GetReplacementValue(Dictionary<string, string> replacementValues, string message)
{
string processedMessage = message;
foreach (var item in replacementValues)
{
processedMessage = processedMessage.Replace(item.Key, item.Value);
}
return processedMessage;
}
Now you only need to generate that Dictionary in a way that makes sense for the current switch case, for example:
case "User":
if (userId.HasValue)
{
var user = _userRepository.GetById(userId);
var replacementValues = sourcekeyWords
.Where(x => processedMessage.Contains(x))
// .Key will be the keyword and .Value will have the replacement value
.ToDictionary(x => x, x => GetPatientInfoFromUser(user, x));
processedMessage = GetReplacementValue(replacementValues, processedMessage);
}
break;

Lambda into a method

I have a method where I'm reading a textfile.
I have to get the words in the textfile which start with "ART".
I have a foreach loop which loops through the method.
class ProductsList
{
public static void Main()
{
String path = #"D:\ProductsProjects\products.txt";
GetProducts(path, s => s.StartsWith("ART"));
//foreach (String productin GetProducts(path, s => s.StartsWith("ART")))
//Console.Write("{0}; ", word);
}
My method looks like this:
public static String GetProducts(String path, Func<String, bool> lambda)
{
try {
using (StreamReader sr = new StreamReader(path)){
string[] products= sr.ReadToEnd().Split(' ');
// need to get all the products starting with ART
foreach (string s in products){
return s;
}
}
}
catch (IOException ioe){
Console.WriteLine(ioe.Message);
}
return ="";
}
}
I'm having problems with the lambda in the method, I'm new to working with lambda's and I don't really know how to apply the lambda in the method.
I'm sorry if I can't really explain myself that well.
just add it here
foreach (string s in products.Where(lambda))
Update:
you should change your method like this to return a list of products and not just a single
public static IEnumerable<string> GetProducts(String path, Func<String, bool> lambda)
{
using (StreamReader sr = new StreamReader(path))
{
string[] products = sr.ReadToEnd().Split(' ');
// need to get all the products starting with ART
foreach (string s in products.Where(lambda))
{
yield return s;
}
}
}
Your code is wrong in that it only ever returns the one string, you want to return multiple strings, if the list of products is large this could also take a while, I'd recommend doing it this way:
public static IEnumerable<string> GetProducts(string path, Func<string, bool> matcher)
{
using(var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None))
{
using(var reader = new StreamReader(stream))
{
do
{
var line = reader.ReadLine();
if (matcher(line)) yield return line
}while(!reader.EndOfFile)
}
}
}
Then using it is as simple as:
foreach(var product in GetProducts("abc.txt", s => s.StartsWith("ART")))
{
Console.WriteLine("This is a matching product: {0}", product);
}
This code has the benefit of returning all of the lines that match the predicate (the lambda), as well as doing so using an iterator block, which means it doesn't actually read the next line until you ask for it.

How to access invocations through extension methods, methods in static classes and methods with ref/out parameters with Roslyn

I'm working on creating an open source project for creating .NET UML Sequence Diagrams that leverages a javascript library called js-sequence-diagrams. I am not sure Roslyn is the right tool for the job, but I thought I would give it a shot so I have put together some proof of concept code which attempts to get all methods and their invocations and then outputs these invocations in a form that can be interpreted by js-sequence-diagrams.
The code generates some output, but it does not capture everything. I cannot seem to capture invocations via extension methods, invocations of static methods in static classes.
I do see invocations of methods with out parameters, but not in any form that extends the BaseMethodDeclarationSyntax
Here is the code (keep in mind this is proof of concept code and so I did not entirely follow best-practices, but I am not requesting a code review here ... also, I am used to using Tasks so I am messing around with await, but am not entirely sure I am using it properly yet)
https://gist.github.com/SoundLogic/11193841
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Emit;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.MSBuild;
using Microsoft.CodeAnalysis.FindSymbols;
using System.Collections.Immutable;
namespace Diagrams
{
class Program
{
static void Main(string[] args)
{
string solutionName = "Diagrams";
string solutionExtension = ".sln";
string solutionFileName = solutionName + solutionExtension;
string rootPath = #"C:\Workspace\";
string solutionPath = rootPath + solutionName + #"\" + solutionFileName;
MSBuildWorkspace workspace = MSBuildWorkspace.Create();
DiagramGenerator diagramGenerator = new DiagramGenerator( solutionPath, workspace );
diagramGenerator.ProcessSolution();
#region reference
//TODO: would ReferencedSymbol.Locations be a better way of accessing MethodDeclarationSyntaxes?
//INamedTypeSymbol programClass = compilation.GetTypeByMetadataName("DotNetDiagrams.Program");
//IMethodSymbol barMethod = programClass.GetMembers("Bar").First(s => s.Kind == SymbolKind.Method) as IMethodSymbol;
//IMethodSymbol fooMethod = programClass.GetMembers("Foo").First(s => s.Kind == SymbolKind.Method) as IMethodSymbol;
//ITypeSymbol fooSymbol = fooMethod.ContainingType;
//ITypeSymbol barSymbol = barMethod.ContainingType;
//Debug.Assert(barMethod != null);
//Debug.Assert(fooMethod != null);
//List<ReferencedSymbol> barReferencedSymbols = SymbolFinder.FindReferencesAsync(barMethod, solution).Result.ToList();
//List<ReferencedSymbol> fooReferencedSymbols = SymbolFinder.FindReferencesAsync(fooMethod, solution).Result.ToList();
//Debug.Assert(barReferencedSymbols.First().Locations.Count() == 1);
//Debug.Assert(fooReferencedSymbols.First().Locations.Count() == 0);
#endregion
Console.ReadKey();
}
}
class DiagramGenerator
{
private Solution _solution;
public DiagramGenerator( string solutionPath, MSBuildWorkspace workspace )
{
_solution = workspace.OpenSolutionAsync(solutionPath).Result;
}
public async void ProcessSolution()
{
foreach (Project project in _solution.Projects)
{
Compilation compilation = await project.GetCompilationAsync();
ProcessCompilation(compilation);
}
}
private async void ProcessCompilation(Compilation compilation)
{
var trees = compilation.SyntaxTrees;
foreach (var tree in trees)
{
var root = await tree.GetRootAsync();
var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>();
foreach (var #class in classes)
{
ProcessClass( #class, compilation, tree, root );
}
}
}
private void ProcessClass(
ClassDeclarationSyntax #class
, Compilation compilation
, SyntaxTree tree
, SyntaxNode root)
{
var methods = #class.DescendantNodes().OfType<MethodDeclarationSyntax>();
foreach (var method in methods)
{
var model = compilation.GetSemanticModel(tree);
// Get MethodSymbol corresponding to method
var methodSymbol = model.GetDeclaredSymbol(method);
// Get all InvocationExpressionSyntax in the above code.
var allInvocations = root.DescendantNodes().OfType<InvocationExpressionSyntax>();
// Use GetSymbolInfo() to find invocations of target method
var matchingInvocations =
allInvocations.Where(i => model.GetSymbolInfo(i).Symbol.Equals(methodSymbol));
ProcessMethod( matchingInvocations, method, #class);
}
var delegates = #class.DescendantNodes().OfType<DelegateDeclarationSyntax>();
foreach (var #delegate in delegates)
{
var model = compilation.GetSemanticModel(tree);
// Get MethodSymbol corresponding to method
var methodSymbol = model.GetDeclaredSymbol(#delegate);
// Get all InvocationExpressionSyntax in the above code.
var allInvocations = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>();
// Use GetSymbolInfo() to find invocations of target method
var matchingInvocations =
allInvocations.Where(i => model.GetSymbolInfo(i).Symbol.Equals(methodSymbol));
ProcessDelegates(matchingInvocations, #delegate, #class);
}
}
private void ProcessMethod(
IEnumerable<InvocationExpressionSyntax> matchingInvocations
, MethodDeclarationSyntax methodDeclarationSyntax
, ClassDeclarationSyntax classDeclarationSyntax )
{
foreach (var invocation in matchingInvocations)
{
MethodDeclarationSyntax actingMethodDeclarationSyntax = null;
if (SyntaxNodeHelper.TryGetParentSyntax(invocation, out actingMethodDeclarationSyntax))
{
var r = methodDeclarationSyntax;
var m = actingMethodDeclarationSyntax;
PrintCallerInfo(
invocation
, classDeclarationSyntax
, m.Identifier.ToFullString()
, r.ReturnType.ToFullString()
, r.Identifier.ToFullString()
, r.ParameterList.ToFullString()
, r.TypeParameterList != null ? r.TypeParameterList.ToFullString() : String.Empty
);
}
}
}
private void ProcessDelegates(
IEnumerable<InvocationExpressionSyntax> matchingInvocations
, DelegateDeclarationSyntax delegateDeclarationSyntax
, ClassDeclarationSyntax classDeclarationSyntax )
{
foreach (var invocation in matchingInvocations)
{
DelegateDeclarationSyntax actingMethodDeclarationSyntax = null;
if (SyntaxNodeHelper.TryGetParentSyntax(invocation, out actingMethodDeclarationSyntax))
{
var r = delegateDeclarationSyntax;
var m = actingMethodDeclarationSyntax;
PrintCallerInfo(
invocation
, classDeclarationSyntax
, m.Identifier.ToFullString()
, r.ReturnType.ToFullString()
, r.Identifier.ToFullString()
, r.ParameterList.ToFullString()
, r.TypeParameterList != null ? r.TypeParameterList.ToFullString() : String.Empty
);
}
}
}
private void PrintCallerInfo(
InvocationExpressionSyntax invocation
, ClassDeclarationSyntax classBeingCalled
, string callingMethodName
, string returnType
, string calledMethodName
, string calledMethodArguments
, string calledMethodTypeParameters = null )
{
ClassDeclarationSyntax parentClassDeclarationSyntax = null;
if (!SyntaxNodeHelper.TryGetParentSyntax(invocation, out parentClassDeclarationSyntax))
{
throw new Exception();
}
calledMethodTypeParameters = calledMethodTypeParameters ?? String.Empty;
var actedUpon = classBeingCalled.Identifier.ValueText;
var actor = parentClassDeclarationSyntax.Identifier.ValueText;
var callInfo = callingMethodName + "=>" + calledMethodName + calledMethodTypeParameters + calledMethodArguments;
var returnCallInfo = returnType;
string info = BuildCallInfo(
actor
, actedUpon
, callInfo
, returnCallInfo);
Console.Write(info);
}
private string BuildCallInfo(string actor, string actedUpon, string callInfo, string returnInfo)
{
const string calls = "->";
const string returns = "-->";
const string descriptionSeparator = ": ";
string callingInfo = actor + calls + actedUpon + descriptionSeparator + callInfo;
string returningInfo = actedUpon + returns + actor + descriptionSeparator + "returns " + returnInfo;
callingInfo = callingInfo.RemoveNewLines(true);
returningInfo = returningInfo.RemoveNewLines(true);
string result = callingInfo + Environment.NewLine;
result += returningInfo + Environment.NewLine;
return result;
}
}
static class SyntaxNodeHelper
{
public static bool TryGetParentSyntax<T>(SyntaxNode syntaxNode, out T result)
where T : SyntaxNode
{
// set defaults
result = null;
if (syntaxNode == null)
{
return false;
}
try
{
syntaxNode = syntaxNode.Parent;
if (syntaxNode == null)
{
return false;
}
if (syntaxNode.GetType() == typeof (T))
{
result = syntaxNode as T;
return true;
}
return TryGetParentSyntax<T>(syntaxNode, out result);
}
catch
{
return false;
}
}
}
public static class StringEx
{
public static string RemoveNewLines(this string stringWithNewLines, bool cleanWhitespace = false)
{
string stringWithoutNewLines = null;
List<char> splitElementList = Environment.NewLine.ToCharArray().ToList();
if (cleanWhitespace)
{
splitElementList.AddRange(" ".ToCharArray().ToList());
}
char[] splitElements = splitElementList.ToArray();
var stringElements = stringWithNewLines.Split(splitElements, StringSplitOptions.RemoveEmptyEntries);
if (stringElements.Any())
{
stringWithoutNewLines = stringElements.Aggregate(stringWithoutNewLines, (current, element) => current + (current == null ? element : " " + element));
}
return stringWithoutNewLines ?? stringWithNewLines;
}
}
}
Any guidance here would be much appreciated!
Using the methodSymbol in the ProcessClass method I took Andy's suggestion and came up with the below (although I imagine there may be an easier way to go about this):
private async Task<List<MethodDeclarationSyntax>> GetMethodSymbolReferences( IMethodSymbol methodSymbol )
{
var references = new List<MethodDeclarationSyntax>();
var referencingSymbols = await SymbolFinder.FindCallersAsync(methodSymbol, _solution);
var referencingSymbolsList = referencingSymbols as IList<SymbolCallerInfo> ?? referencingSymbols.ToList();
if (!referencingSymbolsList.Any(s => s.Locations.Any()))
{
return references;
}
foreach (var referenceSymbol in referencingSymbolsList)
{
foreach (var location in referenceSymbol.Locations)
{
var position = location.SourceSpan.Start;
var root = await location.SourceTree.GetRootAsync();
var nodes = root.FindToken(position).Parent.AncestorsAndSelf().OfType<MethodDeclarationSyntax>();
references.AddRange(nodes);
}
}
return references;
}
and the resulting image generated by plugging the output text into js-sequence-diagrams (I have updated the github gist with the full source for this should anyone find it useful - I excluded method parameters so the diagram was easy digest, but these can optionally be turned back on):
Edit:
I've updated the code (see the github gist) so now calls are shown in the order they were made (based on the span start location of a called method from within the calling method via results from FindCallersAsync):

Synchronous VirusTotal code to Rx enabled asynchronous (and maybe some JSON parsing)

last time I posted a question on here everyone provided some great guidance on getting my problem solved. Move forward in time and here is another. I'm attempting to redo a small helper tool I have that checks URL's and Files against VirusTotal to get some basic information. The code below works quite well but locks up the UI. I was told that I should look into Rx and am enjoying reading up on it but cannot seem to get my head wrapped around it. So now here is where the question comes in, what is the best way to design the following code to make it utilize Rx so that it is asynchronous and leaves my UI alone while it does it's thing. VirusTotal also utilizes multilevel JSON for responses so if anyone has a nice way of integrating that into this that would even be better.
class Virustotal
{
private string APIKey = "REMOVED";
private string FileReportURL = "https://www.virustotal.com/vtapi/v2/file/report";
private string URLReportURL = "http://www.virustotal.com/vtapi/v2/url/report";
private string URLSubmitURL = "https://www.virustotal.com/vtapi/v2/url/scan";
WebRequest theRequest;
HttpWebResponse theResponse;
ArrayList theQueryData;
public string GetFileReport(string checksum) // Gets latest report of file from VT using a hash (MD5 / SHA1 / SHA256)
{
this.WebPostRequest(this.FileReportURL);
this.Add("resource", checksum);
return this.GetResponse();
}
public string GetURLReport(string url) // Gets latest report of URL from VT
{
this.WebPostRequest(this.URLReportURL);
this.Add("resource", url);
this.Add("scan", "1"); //Automatically submits to VT if no result found
return this.GetResponse();
}
public string SubmitURL(string url) // Submits URL to VT for insertion to scanning queue
{
this.WebPostRequest(this.URLSubmitURL);
this.Add("url", url);
return this.GetResponse();
}
public string SubmitFile() // Submits File to VT for insertion to scanning queue
{
// File Upload code needed
return this.GetResponse();
}
private void WebPostRequest(string url)
{
theRequest = WebRequest.Create(url);
theRequest.Method = "POST";
theQueryData = new ArrayList();
this.Add("apikey", APIKey);
}
private void Add(string key, string value)
{
theQueryData.Add(String.Format("{0}={1}", key, Uri.EscapeDataString(value)));
}
private string GetResponse()
{
// Set the encoding type
theRequest.ContentType="application/x-www-form-urlencoded";
// Build a string containing all the parameters
string Parameters = String.Join("&",(String[]) theQueryData.ToArray(typeof(string)));
theRequest.ContentLength = Parameters.Length;
// We write the parameters into the request
StreamWriter sw = new StreamWriter(theRequest.GetRequestStream());
sw.Write(Parameters);
sw.Close();
// Execute the query
theResponse = (HttpWebResponse)theRequest.GetResponse();
StreamReader sr = new StreamReader(theResponse.GetResponseStream());
return sr.ReadToEnd();
}
}
Your code is poorly written which makes it more difficult to make it asynchronous - primarily the three class-level variables. When coding in Rx you want to think "functional programming" and not "OOP" - so no class-level variables.
So, what I've done is this - I've recoded the GetResponse method to encapsulate all of the state into a single call - and I've made it return IObservable<string> rather than just string.
The public functions can now be written like this:
public IObservable<string> GetFileReport(string checksum)
{
return this.GetResponse(this.FileReportURL,
new Dictionary<string, string>() { { "resource", checksum }, });
}
public IObservable<string> GetURLReport(string url)
{
return this.GetResponse(this.URLReportURL,
new Dictionary<string, string>()
{ { "resource", url }, { "scan", "1" }, });
}
public IObservable<string> SubmitURL(string url)
{
return this.GetResponse(this.URLSubmitURL,
new Dictionary<string, string>() { { "url", url }, });
}
public IObservable<string> SubmitFile()
{
return this.GetResponse("UNKNOWNURL", new Dictionary<string, string>());
}
And GetResponse looks like this:
private IObservable<string> GetResponse(
string url,
Dictionary<string, string> theQueryData)
{
return Observable.Start(() =>
{
var theRequest = WebRequest.Create(url);
theRequest.Method = "POST";
theRequest.ContentType="application/x-www-form-urlencoded";
theQueryData.Add("apikey", APIKey);
string Parameters = String.Join("&",
theQueryData.Select(x =>
String.Format("{0}={1}", x.Key, x.Value)));
theRequest.ContentLength = Parameters.Length;
using (var sw = new StreamWriter(theRequest.GetRequestStream()))
{
sw.Write(Parameters);
sw.Close();
}
using (var theResponse = (HttpWebResponse)theRequest.GetResponse())
{
using (var sr = new StreamReader(theResponse.GetResponseStream()))
{
return sr.ReadToEnd();
}
}
});
}
I haven't actually tested this - I don't have the APIKEY for starters - but it should work OK. Let me know how you go.

Asynchronous method with foreach

I have some async method
public static Task<JObject> GetUser(NameValueCollection parameters)
{
return CallMethodApi("users.get", parameters, CallType.HTTPS);
}
And I write method below
public static IEnumerable<JObject> GetUsers(IEnumerable<string> usersUids, Field fields)
{
foreach(string uid in usersUids)
{
var parameters = new NameValueCollection
{
{"uids", uid},
{"fields", FieldsUtils.ConvertFieldsToString(fields)}
};
yield return GetUser(parameters).Result;
}
}
This method is asynchronous? How to write this using Parallel.ForEach?
Something kind of like this.
public static IEnumerable<JObject> GetUsers(IEnumerable<string> usersUids, Field fields)
{
var results = new List<JObject>
Parallel.ForEach(usersUids, uid => {
var parameters = new NameValueCollection
{
{"uids", uid},
{"fields", FieldsUtils.ConvertFieldsToString(fields)}
};
var user = GetUser(parameters).Result;
lock(results)
results.Add(user);
});
return results;
}
NOTE: The results won't be in the same order as you expect.
Your method is not asynchronous. Assuming your GetUser method already starts an asynchronous task, Parallel.ForEach would use additional threads just to start off your tasks, which is probably not what you want.
Instead, what you probably want to do is to start all of the tasks and wait for them to finish:
public static IEnumerable<JObject> GetUsers(IEnumerable<string> usersUids, Field fields)
{
var tasks = usersUids.Select(
uid =>
{
var parameters = new NameValueCollection
{
{"uids", uid},
{"fields", FieldsUtils.ConvertFieldsToString(fields)}
};
return GetUser(parameters);
}
).ToArray();
Task.WaitAll(tasks);
var result = new JObject[tasks.Length];
for (var i = 0; i < tasks.Length; ++i)
result[i] = tasks[i].Result;
return result;
}
If you also want to start them in parallel you can use PLINQ:
var tasks = usersUids.AsParallel().AsOrdered().Select(
uid =>
{
var parameters = new NameValueCollection
{
{"uids", uid},
{"fields", FieldsUtils.ConvertFieldsToString(fields)}
};
return GetUser(parameters);
}
).ToArray();
Both code snippets preserve relative ordering of uids and returned objects - result[0] corresponds to usersUids[0], etc.

Categories

Resources