I'm using Puppeteer-Sharp to download the html of a site, I created a method called GetHtml which return a string that contains the site content. The problem is that when I call the line await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
The application exit without any errors, this is my code:
public class Program
{
public static void Main(string[] args)
{
try
{
new FixtureController().AddUpdateFixtures();
}
catch (Exception ex)
{
new Logger().Error(ex);
}
}
}
public async Task AddFixtures()
{
int monthDays = DateTime.DaysInMonth(DateTime.Now.Year, DateTime.Now.Month);
var days = Enumerable.Range(1, monthDays).Select(x => x.ToString("D2")).ToArray();
HtmlDocument doc = new HtmlDocument(); //this is part of Htmlagilitypack library
foreach (var day in days)
{
//Generate url for this iteration
Uri url = new Uri("somesite/" + day);
var html = await NetworkHelper.GetHtml(url);
doc.LoadHtml(html);
}
}
so each foreach iteration will generate an url which download the data, and the method GetHtml should return the html but the application exit (without errors) when reach var html = .., this is the code of GetHtml:
public static async Task<string> GetHtml(Uri url)
{
try
{
//here the crash
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
}
catch (Exception e)
{
//No breakpoint point firing
}
await new BrowserFetcher().DownloadAsync(BrowserFetcher.DefaultRevision);
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true
});
using (Page page = await browser.NewPageAsync())
{
await page.GoToAsync(url.ToString());
return await page.GetContentAsync();
}
}
Your main method does not wait for the result of your async call. The main method exits, closing the application. To fix it you need to wait for the async method to finish.
If you're using C# 7.1 or newer you can use async Main:
public class Program
{
public static async void Main()
{
await TestAsync();
}
private static async Task TestAsync()
{
await Task.Delay(5000);
}
}
Otherwise you need to wait synchronously:
public class Program
{
public static void Main()
{
TestAsync().GetAwaiter().GetResult();
}
private static async Task TestAsync()
{
await Task.Delay(5000);
}
}
Related
If I call Stop(), OperationCanceledException is happened and _writer.TryComplete(exp) is true. But _reader.Completion Task is still not completed.
Is it desired behavior of Channels? If yes can someone tell me how to stop a Channel without waiting till it's empty and have its Completion Task in Completed state?
public interface IItem
{
Uri SourceUri { get; }
string TargetPath { get; }
}
public class Item : IItem
{
public Item(Uri sourceUri, string targetPath)
{
SourceUri = sourceUri;
TargetPath = targetPath;
}
public Uri SourceUri { get; }
public string TargetPath { get; }
}
public class TestService
{
private readonly ChannelWriter<IItem> _writer;
private readonly ChannelReader<IItem> _reader;
private readonly CancellationTokenSource _cts;
public TestService()
{
_cts = new CancellationTokenSource();
Channel<IItem> channel = Channel.CreateUnbounded<IItem>();
_reader = channel.Reader;
_writer = channel.Writer;
}
public async Task QueueDownload(IItem information)
{
await _writer.WriteAsync(information);
}
public void StartDownload()
{
Task.Factory.StartNew(async () =>
{
await ProcessDownloadAsync();
}, TaskCreationOptions.LongRunning);
}
public void Stop()
{
_cts.Cancel();
//_writer.Complete();
//_writer = null;
Console.WriteLine("Stop");
}
public async Task Wait()
{
await _reader.Completion;
}
private async Task ProcessDownloadAsync()
{
try
{
while (await _reader.WaitToReadAsync(_cts.Token))
{
IItem information = await _reader.ReadAsync(_cts.Token);
using (WebClient webClient = new WebClient())
{
Console.WriteLine(information.TargetPath);
await webClient.DownloadFileTaskAsync(information.SourceUri,
information.TargetPath);
}
}
}
catch (OperationCanceledException exp)
{
bool res = _writer.TryComplete(exp);
}
}
}
static class Program
{
static async Task Main(string[] args)
{
TestService tSvc = new TestService();
await tSvc.QueueDownload(new Item(new Uri(#"https://images.pexels.com/" +
#"photos/753626/pexels-photo-753626.jpeg"), #"D:\\Temp\1.png"));
await tSvc.QueueDownload(new Item(new Uri(#"https://images.pexels.com/" +
#"photos/753626/pexels-photo-753626.jpeg"), #"D:\\Temp\1.png"));
await tSvc.QueueDownload(new Item(new Uri(#"https://images.pexels.com/" +
#"photos/753626/pexels-photo-753626.jpeg"), #"D:\\Temp\1.png"));
await tSvc.QueueDownload(new Item(new Uri(#"https://images.pexels.com/" +
#"photos/753626/pexels-photo-753626.jpeg"), #"D:\\Temp\1.png"));
tSvc.StartDownload();
Task t = tSvc.Wait();
tSvc.Stop();
await t;
Console.WriteLine("Finished");
}
}
The ChannelWriter.Complete method behaves a bit differently than one would expect. It is not invalidating instantly the contents of the channel. Instead, it just prevents adding more items in the channel. The existing items are still valid for consumption, and the ChannelReader.Completion property will not complete before all stored items are consumed.
The example below demonstrates this behavior:
var channel = Channel.CreateUnbounded<int>();
channel.Writer.TryWrite(1);
channel.Writer.Complete(new FileNotFoundException());
//channel.Reader.TryRead(out var data);
var completed = channel.Reader.Completion.Wait(500);
Console.WriteLine($"Completion: {(completed ? "OK" : "Timed-out")}");
Output:
Completion: Timed-out
You can uncomment the channel.Reader.TryRead line, to see the FileNotFoundException to emerge.
I have implemented a soap client using a Async method. I want this method to return a string value that I get from the API server to my main Thread or to another method (whichever method is calling). How do I do this:
MAIN THREAD
static void Main(string[] args)
{
TEXT().GetAwaiter().OnCompleted(() => { Console.WriteLine("finished"); });
Console.ReadKey();
// if I do it like this
// var test = TEXT().GetAwaiter().OnCompleted(() => { Console.WriteLine("finished"); });
// it gives me error: Cannot assign void to an implicitly-typed local variable
}
ASYNC METHOD
public static async Task<string> TEXT()
{
Uri uri = new Uri("http://myaddress");
HttpClient hc = new HttpClient();
hc.DefaultRequestHeaders.Add("SOAPAction", "Some Action");
var xmlStr = "SoapContent"; //not displayed here for simplicity
var content = new StringContent(xmlStr, Encoding.UTF8, "text/xml");
using (HttpResponseMessage response = await hc.PostAsync(uri, content))
{
var soapResponse = await response.Content.ReadAsStringAsync();
string value = await response.Content.ReadAsStringAsync();
return value; //how do I get this back to the main thread or any other method
}
}
In a pre-C# 7.0 console application it can be achieved as simple as this:
public static void Main()
{
string result = TEXT().Result;
Console.WriteLine(result);
}
In this case TEXT can be considered a usual method, which returns Task<string>, so its result is available in Result property. You don't need to mess with awaiter, results etc.
At the same time, you cannot do this in most types of applications (WinForms, WPF, ASP.NET etc.) and in this case you will have to use async/await across all your application:
public async Task SomeMethod()
{
string result = await TEXT();
// ... do something with result
}
If you plan to do a lot of async in a console application, I recommend using this sort of MainAsync pattern:
static public void Main(string[] args) //Entry point
{
MainAsync(args).GetAwaiter().GetResult();
}
static public Task MainAsync(string[] args) //Async entry point
{
await TEXT();
Console.WriteLine("finished");
}
If you upgrade to C# 7.1 or later, you can then remove the Main method and use async main.
Or if you ever migrate this code to an ASP.NET or WinForms application, you can ignore Main and migrate the code in MainAsync (otherwise you will run afoul of the synchronization model and get deadlocked).
In C# 7.0+, you can use async Task Main
static async Task Main(string[] args)
{
var result = TEXT().ConfigureAwait(false)
Console.ReadKey();
}
for older versions of C#
public static void Main(string[] args)
{
try
{
TEST().GetAwaiter().GetResult();
}
catch (Exception ex)
{
WriteLine($"There was an exception: {ex.ToString()}");
}
}
I'm working on a code that regularly screenshots a web page for an image, scans the pixel colors of the image, and if a color is found, connects asynchronously to a web API.
I have worked out how to do the color scanning and the connection separately and now I have to join the two logics but I'm not sure of the best way to do it .
The scanning web page/image scan code is essentially this:
static void Main(string[] args)
{
while (true)
{
try
{
System.Threading.Thread.Sleep(5000); //refresh speed
string color = ReadColor(driver, webElement);
if (color== "blue")
{
//should connect for blue case and run the Blue() function below
}
if (color== "green")
{
//should connect for green case
}
}
catch
{
}
}
}
the HttpClient connection goes like this:
static HttpClient client = new HttpClient();
static void Main()
{
RunAsync().Wait();
}
static async Task RunAsync()
{
client.BaseAddress = new Uri("website");
Data data = new Data { };
try
{
data = await Green();
data = await blue(); //functions to run depending on color
}
catch (Exception e)
{
}
}
how should the RunAsync().Wait(); method be properly inserted in the first example and how should I call the correct function?
EDIT:
ok so eventually I'm doing it this way:
static void Main(string[] args)
{
while (true)
{
try
{
System.Threading.Thread.Sleep(1000);
string signal = ReadGraph(driver, webElement); //////READ CHART
if (signal == "blue")
{
Task.Run(async () => await RunAsync(signal)).Wait();
}
if (signal == "green")
{
RunAsync(signal);
}
}
catch
{
}
}
}
however, whenever the RunAsync is called, it executes but the thread never returns to the main loop.
I tried
Task.Run(async () => await RunAsync(signal)).Wait()
Task.Run(async () => await RunAsync(signal)).Wait()
RunAsync(signal);
RunAsync(signal).wait();
with the same result, what am I doing wrong?
This is how you should do it if you want async:
static async void Main(string[] args)
{
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(1.0));
string signal = ReadGraph(driver, webElement);
if (signal == "blue")
{
await RunAsync(signal);
}
if (signal == "green")
{
await RunAsync(signal);
}
}
}
I am trying to use the concept of async and await in my program. The program abruptly exits. I am trying to get the content length from few random urls and process it and display the size in bytes of each url.
Code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
namespace TestProgram
{
public class asyncclass
{
public async void MainCall() {
await SumPageSizes();
}
public async Task SumPageSizes(){
List<string> urllist = GetUrlList();
foreach (var url in urllist)
{
byte[] content = await GetContent(url);
Displayurl(content, url);
}
}
private void Displayurl(byte[] content, string url)
{
var length = content.Length;
Console.WriteLine("The bytes length for the url response " + url + " is of :" +length );
}
private async Task<byte[]> GetContent(string url)
{
var content = new MemoryStream();
try
{
var obj = (HttpWebRequest)WebRequest.Create(url);
WebResponse response = obj.GetResponse();
using (Stream stream = response.GetResponseStream())
{
await stream.CopyToAsync(content);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
return content.ToArray();
}
private List<string> GetUrlList()
{
var urllist = new List<string>(){
"http://msdn.microsoft.com/library/windows/apps/br211380.aspx",
"http://msdn.microsoft.com",
"http://msdn.microsoft.com/en-us/library/hh290136.aspx",
"http://msdn.microsoft.com/en-us/library/ee256749.aspx",
"http://msdn.microsoft.com/en-us/library/hh290138.aspx",
"http://msdn.microsoft.com/en-us/library/hh290140.aspx",
"http://msdn.microsoft.com/en-us/library/dd470362.aspx",
"http://msdn.microsoft.com/en-us/library/aa578028.aspx",
"http://msdn.microsoft.com/en-us/library/ms404677.aspx",
"http://msdn.microsoft.com/en-us/library/ff730837.aspx"
};
return urllist;
}
}
}
Main
public static void Main(string[] args)
{
asyncclass asyncdemo = new asyncclass();
asyncdemo.MainCall();
}
MainCall returns an uncompleted task and no other line of code is present beyond that, so your program ends
To wait for it use:
asyncdemo.MainCall().Wait();
You need to avoid async void and change MainCall to async Task in order to be able to wait for it from the caller.
Since this seems to be a console application, you can't use the await and async for the Main method using the current version of the compiler (I think the feature is being discussed for upcoming implementation in C# 7).
The problem is that you don't await an asynchron method and therefore you application exits before the method ended.
In c# 7 you could create an async entry point which lets you use the await keyword.
public static async Task Main(string[] args)
{
asyncclass asyncdemo = new asyncclass();
await asyncdemo.MainCall();
}
If you want to bubble your exceptions from MainCall you need to change the return type to Task.
public async Task MainCall()
{
await SumPageSizes();
}
If you wanted to run your code async before c# 7 you could do the following.
public static void Main(string[] args)
{
asyncclass asyncdemo = new asyncclass();
asyncdemo.MainCall().Wait();
// or the following line if `MainCall` doesn't return a `Task`
//Task.Run(() => MainCall()).Wait();
}
You have to be very careful when using async void methods. Those will not be awaited. One normal example of an async void is when you are calling an awaitable method inside a button click:
private async void Button_Click(object sender, RoutedEventArgs e)
{
// run task here
}
This way the UI won't be stuck waiting for the button click to complete.
On most custom methods you will almost always want to return a Task so that you are able to know when your method is finished.
In my quest to create the perfect string result = browser.Browse(url) method, I have created a simple class library to demonstrate CefSharp. The code for that is:
public class CefSharpHeadlessBrowser
{
public CefSharpHeadlessBrowser()
{
Cef.Initialize(new CefSettings { CachePath = "cache" }, false, true);
}
public string Browse(string url)
{
Task<string> result;
var browserSettings = new BrowserSettings { WindowlessFrameRate = 1 };
using (var browser = new ChromiumWebBrowser(url, browserSettings))
{
browser.WaitForBrowserToInitialize();
browser.LoadPageAsync();
// Wait awhile for Javascript to finish executing.
Thread.Sleep(2000);
result = browser.GetSourceAsync();
Thread.Sleep(100);
}
return result.Result;
}
}
public static class CefExtensions
{
public static void WaitForBrowserToInitialize(this ChromiumWebBrowser browser)
{
while (!browser.IsBrowserInitialized)
{
Task.Delay(100);
}
}
public static Task LoadPageAsync(this IWebBrowser browser)
{
var tcs = new TaskCompletionSource<bool>();
EventHandler<LoadingStateChangedEventArgs> handler = null;
handler = (sender, args) =>
{
if (!args.IsLoading)
{
browser.LoadingStateChanged -= handler;
tcs.TrySetResult(true);
}
};
browser.LoadingStateChanged += handler;
return tcs.Task;
}
}
This is the test harness, in a separate console project that references the CefSharpHeadlessBrowser project:
class Program
{
static void Main(string[] args)
{
const string searchUrl = "https://www.google.com";
var browser = new CefSharpHeadlessBrowser();
var result = browser.Browse(searchUrl);
Console.Write(result);
}
}
This actually works; it gets the HTML page source properly and displays it in the console window, just as it should. But here's the problem: the console program hangs after displaying the page source. It should exit immediately. Which must mean that I'm doing something wrong with the asynchronous operations and causing a deadlock.
What could be the issue?
CefSharp has a Shutdown command; I was able to solve the problem by adding the following method to the CefSharpHeadlessBrowser class:
public void Shutdown()
{
Cef.Shutdown();
}
And then changing the Test Harness to:
class Program
{
static void Main(string[] args)
{
const string searchUrl = "https://www.google.com";
var browser = new CefSharpHeadlessBrowser();
var result = browser.Browse(searchUrl);
Console.WriteLine(result);
browser.Shutdown(); // Added
}
}
This undoubtedly frees up any remaining threads that are running.
I'll probably make the class IDisposable and wrap the calling code in a using statement.