System.NullReferenceException when reading browser log with selenium - c#

I am writing using C#, selenium chromeWebDriver. When I try to read the browser console log file with selenium I get:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
private void button1_Click(object sender, EventArgs e)
{
ChromeOptions options = new ChromeOptions();
options.SetLoggingPreference(LogType.Browser, LogLevel.Warning);
IWebDriver driver = new ChromeDriver(options);
driver.Url = "https://www.google.com/";
var entries = driver.Manage().Logs.GetLog(LogType.Browser); // System.NullReferenceException
foreach (var entry in entries)
{
Console.WriteLine(entry.ToString());
}
}

This is my solution until Selenium 4 is out (will work also with Selenium 4).
It is quick and dirty and was design to demonstrate how it can be done. Feel free to alter and improve.
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Remote;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Reflection;
using System.Text;
namespace GetChromeConsoleLog
{
internal static class Program
{
private static void Main()
{
// setup options
var options = new ChromeOptions();
options.SetLoggingPreference(LogType.Browser, LogLevel.All);
// do whatever actions
var driver = new ChromeDriver(options)
{
Url = "https://www.yahoo.com/"
};
var logs = driver.GetBrowserLogs();
// extract logs (using the extension method GetBrowserLogs)
foreach (var log in driver.GetBrowserLogs())
{
Console.WriteLine($"{log["timestamp"]}: {log["message"]}");
}
// cleanup
driver.Dispose();
// hold console
Console.WriteLine();
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
}
public static class WebDriverExtensions
{
public static IEnumerable<IDictionary<string, object>> GetBrowserLogs(this IWebDriver driver)
{
// not a chrome driver
if(driver.GetType() != typeof(ChromeDriver))
{
return Array.Empty<IDictionary<string, object>>();
}
// setup
var endpoint = GetEndpoint(driver);
var session = GetSession(driver);
var resource = $"{endpoint}session/{session}/se/log";
const string jsonBody = #"{""type"":""browser""}";
// execute
using (var httpClient = new HttpClient())
{
var content = new StringContent(jsonBody, Encoding.UTF8, "application/json");
var response = httpClient.PostAsync(resource, content).GetAwaiter().GetResult();
var responseBody = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return AsLogEntries(responseBody);
}
}
private static string GetEndpoint(IWebDriver driver)
{
// setup
const BindingFlags Flags = BindingFlags.Instance | BindingFlags.NonPublic;
// get RemoteWebDriver type
var remoteWebDriver = GetRemoteWebDriver(driver.GetType());
// get this instance executor > get this instance internalExecutor
var executor = remoteWebDriver.GetField("executor", Flags).GetValue(driver) as ICommandExecutor;
// get URL
var uri = executor.GetType().GetField("remoteServerUri", Flags).GetValue(executor) as Uri;
// result
return uri.AbsoluteUri;
}
private static Type GetRemoteWebDriver(Type type)
{
if (!typeof(RemoteWebDriver).IsAssignableFrom(type))
{
return type;
}
while (type != typeof(RemoteWebDriver))
{
type = type.BaseType;
}
return type;
}
private static SessionId GetSession(IWebDriver driver)
{
if (driver is IHasSessionId id)
{
return id.SessionId;
}
return new SessionId($"gravity-{Guid.NewGuid()}");
}
private static IEnumerable<IDictionary<string, object>> AsLogEntries(string responseBody)
{
// setup
var value = $"{JToken.Parse(responseBody)["value"]}";
return JsonConvert.DeserializeObject<IEnumerable<Dictionary<string, object>>>(value);
}
}
}
get logs

I have previously used the property above but I cannot get it to work currently with the ChromeDriver 75+. I found issues related to it reported here.

This issue was supposedly fixed as it was reported in GitHub issue #7323 here, but I have actually attempted to test this fix in ChromeDriver Nuget version 77.0.3865.4000 and it still proves to be an issue.
Further experimentation with newer Chromedriver version 78.0.3904.7000 (currently Latest Stable version at the time of writing) shows that the issue still exists.
I have also experimented with using workarounds provided in other Selenium issue #7335 here back in September, and while this workaround does allow the driver to be instantiated, the logs are still inaccessible (and null).
Workaround when creating chromedriver instance: typeof(CapabilityType).GetField(nameof(CapabilityType.LoggingPreferences), BindingFlags.Static | BindingFlags.Public).SetValue(null, "goog:loggingPrefs");
Based on what MatthewSteeples said in that issue (see quote below), the fix is in place just not yet fully released to Nuget. Hopefully it will come in with the next release.
"The issue has been resolved but the fix is not (yet) available on NuGet so you'll need to roll your own if you need it before the next release is out" - MatthewSteeples September 25'th 2019
Edit: It may be worth mentioning the reason for using an older ChromeDriver Nuget is so that running automated tests locally and in the Hosted Azure Devops release pipeline is possible without manually modifying the nuget version locally.

Related

How do I run a test on multiple browsers at the same time? Selenium Grid, C#, Specflow, NUnit

I have been bouncing between guides and YouTube videos trying to implement Selenium Grid 2 on an existing project for a couple of days and I've gotten stuck, please help!
Our framework is Specflow 3.0.220, Selenium WebDriver 3.141.0, C#, NUnit 3.12.0, Selenium Grid selenium-server-standalone-3.141.59.
My initial objectives to implement Selenium Grid 2 are as follows:
Set up a hub and node(s) on my local machine = done.
Run a test through one of the nodes = done.
Run a test on all nodes simultaneously = headache.
Regarding item 2, I have set up two nodes, one is a Chrome node and one is a Firefox node. I can run a test through both of them, but not simultaneously.
I feel that I missing a piece of the puzzle here.
Here's the set-up:
Scenario Outline: Log in
Given I launch the site for <profile> and <environment> and <parallelEnvironment>
When I log in to the Normal account
Then I see that I am logged in
Examples:
| profile | environment | parallelEnvironment |
| parallel | Chrome75 | grid |
If profile is parallel and parallelEnvironment is grid, environment is ignored. The reason for parallelEnvironment is because we may still use Browserstack in the interim while setting up Selenium Grid.
These steps use relevant step files etc and page files (but not using Page Object Model as that's been deprecated).
The driver set-up is as follows:
namespace OurAutomation
{
[Binding]
public sealed class BrowserStack
{
private BrowserStackDriver bsDriver;
public static BrowserStackDriver bdriver;
[BeforeScenario]
public void BeforeScenario()
{
bsDriver = new BrowserStackDriver();
bdriver = bsDriver;
}
[AfterScenario]
public void AfterScenario()
{
bsDriver.Cleanup();
}
}
public class CustomRemoteWebDriver : RemoteWebDriver
{
public CustomRemoteWebDriver(Uri remoteAddress, ChromeOptions options) : base(remoteAddress, options)
{
}
public string getSessionID()
{
return base.SessionId.ToString();
}
}
public class BrowserStackDriver
{
private IWebDriver driver;
public static bool isBrowserStack = false;
public static string Platform;
public static string theEnvironment;
public static string sessionId;
public BrowserStackDriver()
{
}
public string GetString(string property)
{
if (TestContext.Parameters[property] == null)
{
throw new ArgumentException("Property does not exist, does not have a value, or a test setting is not selected. You may need to add the .runsettings file in Visual Studio (Test > Test Settings > Select Test Settings File).");
}
else
{
return TestContext.Parameters[property].ToString();
}
}
public IWebDriver Init(string profile, string environment, string parallelEnvironment)
{
String testString = GetString("BuildNumber");
theEnvironment = environment;
NameValueCollection caps = ConfigurationManager.GetSection("capabilities/" + profile) as NameValueCollection;
NameValueCollection settings = ConfigurationManager.GetSection("environments/" + environment) as NameValueCollection;
ChromeOptions chromeOptions = new ChromeOptions();
if (profile == "single")
{
// logic to invoke relevant browser locally based on Specflow parameter 'profile'
Thread.Sleep(3000);
}
else if (profile == "parallel")
{
if (parallelEnvironment == "browserstack")
{
foreach (string key in caps.AllKeys)
{
chromeOptions.AddAdditionalCapability(key, caps[key]);
}
foreach (string key in settings.AllKeys)
{
chromeOptions.AddAdditionalCapability(key, settings[key]);
}
string username = Environment.GetEnvironmentVariable("BROWSERSTACK_USERNAME");
if (username == null)
{
username = ConfigurationManager.AppSettings.Get("user");
}
string accesskey = Environment.GetEnvironmentVariable("BROWSERSTACK_ACCESS_KEY");
if (accesskey == null)
{
accesskey = ConfigurationManager.AppSettings.Get("key");
}
chromeOptions.AddAdditionalCapability("browserstack.user", username);
chromeOptions.AddAdditionalCapability("browserstack.key", accesskey);
chromeOptions.AddAdditionalCapability("browserstack.local", "true");
chromeOptions.AddAdditionalCapability("build", GetString("BuildNumber"));
chromeOptions.AddAdditionalCapability("name", TestContext.CurrentContext.Test.MethodName);
chromeOptions.AddAdditionalCapability("project", GetString("Project"));
BrowserStackDriver.isBrowserStack = true;
driver = new CustomRemoteWebDriver(
new Uri("http://" + ConfigurationManager.AppSettings.Get("server") + "/wd/hub/"), chromeOptions);
CustomRemoteWebDriver browserRemoteDriver = driver as CustomRemoteWebDriver;
sessionId = browserRemoteDriver.getSessionID();
}
else if (parallelEnvironment == "grid")
{
driver = new RemoteWebDriver(new Uri("http://000.00.00.00:4444/wd/hub"), chromeOptions);
}
}
return driver;
}
public void Cleanup()
{
Thread.Sleep(2000);
if (isBrowserStack)
{
Log.Status status = (TestContext.CurrentContext.Result.Message == null) ? Log.Status.Passed : Log.Status.Failed;
string reason = (TestContext.CurrentContext.Result.Message == null) ? "Passed" : "Error see exception";
Log.UpdateTestStatus(status, reason, sessionId);
}
driver.Quit();
driver = null;
}
}
}
So in here...
else if (parallelEnvironment == "grid")
{
driver = new RemoteWebDriver(new Uri("http://000.00.00.00:4444/wd/hub"), chromeOptions);
}
...I enter the address of one of the nodes and the test gets conducted. However, I just want to send the test to the hub and for it to then execute that one test on all active nodes in their related browsers simultaneously. How do I achieve that? The guides and videos only seem to be taking me so far.
thank you
UPDATE:
So I'm inching further in the right direction I think. Had to roll this back to basics, so I can see how to implement this in my existing project. I've made this work in my grid: https://github.com/teixeira-fernando/Parallel-Execution-with-Selenium-Grid
However I note that I need to add attributes to the tests (to run one test on multiple browsers simultaneously)...
namespace Tutorial_parallel_execution
{
[TestFixture(BrowserType.Chrome)]
[TestFixture(BrowserType.Firefox)]
[TestFixture(BrowserType.Opera)]
[TestFixture(BrowserType.IE)]
[Parallelizable(ParallelScope.Fixtures)]
public class GoogleTesting : Hooks
{
public GoogleTesting(BrowserType browser) : base(browser)
{
}
[Test]
public void GoogleTest()
{
Driver.Navigate().GoToUrl("http://www.google.com");
Driver.FindElement(By.Name("q")).SendKeys("selenium");
Driver.FindElement(By.Name("btnK")).Click();
Assert.That(Driver.PageSource.Contains("Selenium"), Is.EqualTo(true),
"The text selenium doenst exist");
}
}
}
However, since my project started complaining similarly to this SpecFlow Visual Studio extension attempted to use SpecFlow code-behind generator 1.9, I started using SpecFlow.Tools.MsBuild.Generation and lost access to the tests (the code-behind files) in order to add the attributes. The only attribute I can add is [Parallelizable(ParallelScope.Fixtures)] but I have to put this in AssemblyInfo.cs - the other attributes can't be added there.
Do I need to be downgrading the versions of Specflow/Selenium etc in order to make this work??
I was able to strip out the code necessary to implement parallel execution using ThreadLocal from https://github.com/minhhoangvn/AutomationFramework
Add this to your AssemblyInfo.cs file:
[assembly: Parallelizable(ParallelScope.Fixtures)]
[assembly: LevelOfParallelism(4)]
Where you see 4 is the number of tests you want to run at the same time. So if you have 2 nodes, but you want to run 4 tests at the same time, then each node will get 2 chrome browsers.
When you use MsBuild.Generation the feature.cs files are still there, they just don't show up in visual studio.
You could try adding this to your Hooks.cs file when creating the driver:
ScenarioContext _scenarioContext;
IWebDriver _currentWebDriver;
_currentWebDriver = new RemoteWebDriver(new Uri(Utilities.SeleniumHub), options.ToCapabilities(), TimeSpan.FromMinutes(3));
_scenarioContext.ScenarioContainer.RegisterInstanceAs<IWebDriver>(_currentWebDriver);
And then this when you are done with the scenario:
[AfterScenario]
public void CloseBrowserAfterScenario()
{
string driver_process_name = null;
string browser_process_name = null;
switch (browser)
{
case "Chrome":
driver_process_name = "chromedriver.exe";
break;
case "IEX64":
case "IEX86":
driver_process_name = "IEDriverServer.exe";
break;
case "Edge":
driver_process_name = "MicrosoftWebDriver.exe";
browser_process_name = "MicrosoftEdge.exe";
break;
case "Firefox":
driver_process_name = "geckodriver.exe";
break;
default:
LogMessage(browser + "is not found or not supported... Please update the TestUI.dll.Config File");
break;
}
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName(driver_process_name);
foreach (System.Diagnostics.Process app_process in process)
{
if (!string.IsNullOrEmpty(app_process.ProcessName))
{
try
{
app_process.Kill();
}
catch
{
FunctionalUtil.LogMessage("app_process.Kill(); failed in CloseBrowserAfterScenario");
}
}
}

How to make asynchronous calls using HtmlAgilityPack?

I'm trying to get the table with id table-matches available here. The problem is that table is loaded using ajax so I don't get the full html code when I download the page:
string url = "http://www.oddsportal.com/matches/soccer/20180701/";
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = client.GetAsync(url).Result)
{
using (HttpContent content = response.Content)
{
string result = content.ReadAsStringAsync().Result;
}
}
}
the html returned does not contains any table, so I tried to see if there is a problem of the library, infact I setted on Chrome (specifically on the Dev console F12) javascript off and same result on the browser.
Fox fix this problem I though to use a WebBrowser, in particular:
webBrowser.Navigate("oddsportal.com/matches/soccer/20140221/");
HtmlElementCollection elements = webBrowser.Document.GetElementsByTagName("table");
but I want ask if I can load also the full html doing asynchronus calls, someone has encountered a similar problem?
Could you please share a solution? Thanks.
The main issue with this page is that content inside table-matches is loaded via ajax. And neither HttpClient nor HtmlAgilityPack unable to wait for ajax to be executed. Therefore, you need different approach.
Approach #1 - Use any headless browser like PuppeteerSharp
using PuppeteerSharp;
using System;
using System.Threading.Tasks;
namespace PuppeteerSharpDemo
{
class Program
{
private static String url = "http://www.oddsportal.com/matches/soccer/20180701/";
static void Main(string[] args)
{
var htmlAsTask = LoadAndWaitForSelector(url, "#table-matches .table-main");
htmlAsTask.Wait();
Console.WriteLine(htmlAsTask.Result);
Console.ReadKey();
}
public static async Task<string> LoadAndWaitForSelector(String url, String selector)
{
var browser = await Puppeteer.LaunchAsync(new LaunchOptions
{
Headless = true,
ExecutablePath = #"c:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
});
using (Page page = await browser.NewPageAsync())
{
await page.GoToAsync(url);
await page.WaitForSelectorAsync(selector);
return await page.GetContentAsync();
}
}
}
}
In purpose of cleanness, I've posted output here here. And once you get html content you are able to parse it with HtmlAgilityPack.
Approach #2 - Use pure Selenium WebDriver. Can be launched in headless mode.
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System;
namespace SeleniumDemo
{
class Program
{
private static IWebDriver webDriver;
private static TimeSpan defaultWait = TimeSpan.FromSeconds(10);
private static String targetUrl = "http://www.oddsportal.com/matches/soccer/20180701/";
private static String driversDir = #"../../Drivers/";
static void Main(string[] args)
{
webDriver = new ChromeDriver(driversDir);
webDriver.Navigate().GoToUrl(targetUrl);
IWebElement table = webDriver.FindElement(By.Id("table-matches"));
var innerHtml = table.GetAttribute("innerHTML");
}
#region (!) I didn't even use this, but it can be useful (!)
public static IWebElement FindElement(By by)
{
try
{
WaitForAjax();
var wait = new WebDriverWait(webDriver, defaultWait);
return wait.Until(driver => driver.FindElement(by));
}
catch
{
return null;
}
}
public static void WaitForAjax()
{
var wait = new WebDriverWait(webDriver, defaultWait);
wait.Until(d => (bool)(d as IJavaScriptExecutor).ExecuteScript("return jQuery.active == 0"));
}
#endregion
}
}
Approach #3 - Simulate ajax requests
If you analyse the page loading using Fiddler or browser's profiler (F12) you can see that all data is coming with these two requests:
So you can try to execute them directly using HttpClient. But in this case you may need to track authorization headers and maybe something else with each HTTP request.

Selenium. Bring-up window on the front

If you run the following code, then at each iteration of the cycle, the browser will bring up on the front and get focus.
public class Program
{
private static void Main()
{
var driver = new ChromeDriver();
driver.Navigate().GoToUrl("https://i.imgur.com/cdA7SBB.jpg");
for (int i = 0; i < 100; i++)
{
var ss = ((ITakesScreenshot)driver).GetScreenshot();
ss.SaveAsFile("D:/imgs/i.jpg");
}
}
}
The question is: why does this happen and can it be turned off? headless mod does not fit.
It seems that this always happens when Selenium needs to save / read the file or start the process.
To take a screenshot, chromedriver activates the window. It's by design and there's no option to avoid it even though it's technically possible.
For the relevant sources have a look at window_commands.cc.
You could however avoid the effect by moving the window off-screen:
driver.Manage().Window.Position = new Point(-32000, -32000);
or by launching the browser off-screen:
var options = new ChromeOptions();
options.AddArgument("--window-position=-32000,-32000");
UPDATE
You can avoid the activation by taking the screenshot directly via the devtool API. Here's a class to override GetScreenshot:
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Remote;
using JObject = System.Collections.Generic.Dictionary<string, object>;
class ChromeDriverEx : ChromeDriver
{
public ChromeDriverEx(ChromeOptions options = null)
: base(options ?? new ChromeOptions()) {
var repo = base.CommandExecutor.CommandInfoRepository;
repo.TryAddCommand("send", new CommandInfo("POST", "/session/{sessionId}/chromium/send_command_and_get_result"));
}
public new Screenshot GetScreenshot() {
object response = Send("Page.captureScreenshot", new JObject {{"format", "png"}, {"fromSurface", true}});
string base64 = (string)((JObject)response)["data"];
return new Screenshot(base64);
}
protected object Send(string cmd, JObject args) {
return this.Execute("send", new JObject {{"cmd", cmd}, {"params", args}}).Value;
}
}
usage:
var driver = new ChromeDriverEx();
driver.Url = "https://stackoverflow.com";
driver.GetScreenshot().SaveAsFile("/tmp/screenshot.png");
driver.Quit();
When you invoke Navigate().GoToUrl("url") method through your Automation script, it is expected that your script will be interacting with some of the elements on the webpage. So for Selenium to interact with those elements, Selenium needs focus. Hence opening up the browser, bring up on the front and getting the focus is the default phenomenon implemented through Navigate().GoToUrl("url").
Now Default Mode or Headless Mode is controlled by the ChromeOption/FirefoxOptions class which is passed as an argument while initializing the WebDriver instance and will call Navigate().GoToUrl("url"). So, Navigate().GoToUrl("url") would have no impact how the WebDriver instance is controlling the Mode of Operation i.e. Default Mode or Headless Mode.
Now when you try to invoke the method from ITakesScreenshot Interface i.e. ITakesScreenshot.GetScreenshot Method which is defined as :
Gets a Screenshot object representing the image of the page on the screen.
In case of WebDriver instance which extends ITakesScreenshot, makes the best effort depending on the browser to return the following in order of preference:
Entire page
Current window
Visible portion of the current frame
The screenshot of the entire display containing the browser
There may be some instances when the browser looses the focus. In that case you can use IJavascriptExecutor to regain the focus as follows :
((IJavascriptExecutor) driver).executeScript("window.focus();");
I was struggling with an issue when generic GetScreenshot() in parallel testing was causing browser to lose focus. Some elements were being removed from DOM and my tests were failing. I've come up with a working solution for Edge and Chrome 100+ with Selenium 4.1:
public Screenshot GetScreenshot()
{
IHasCommandExecutor executor = webDriverInstance as IHasCommandExecutor;
var sessionId = ((WebDriver)webDriverInstance).SessionId;
var command = new HttpCommandInfo(HttpCommandInfo.PostCommand, $"/session/{sessionId}/chromium/send_command_and_get_result");
executor.CommandExecutor.TryAddCommand("Send", command);
var response = Send(executor, "Page.captureScreenshot", new JObject { { "format", "png" }, { "fromSurface", true } });
var base64 = ((Dictionary<string, object>)response.Value)["data"];
return new Screenshot(base64.ToString());
}
private Response Send(IHasCommandExecutor executor, string cmd, JObject args)
{
var json = new JObject { { "cmd", cmd }, { "params", args } };
var command = new Command("Send", json.ToString());
return executor.CommandExecutor.Execute(command);
}

Tring to get GitHub repo via Octokit

I'm building simple tool for downloading .lua files from online public GitHub repos via link given by user. I started learning async methods so I wanted to test myself.
It's a console application (for now). The ultimate goal is to get .lua files in a repo and ask the user which ones he wants downloaded, but I'll be happy if I connect to GH for now.
I'm using Octokit (https://github.com/octokit/octokit.net) GitHub API integration to .NET.
This is the reduced code; I removed some of unimportant stuff:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Octokit;
namespace GetThemLuas
{
class Program
{
static readonly GitHubClient Github = new GitHubClient(new ProductHeaderValue ("Testing123"), new Uri("https://www.github.com/"));
static void Main(string[] args)
{
Console.WriteLine("Welcome to GitHub repo downloader");
GetRepoTry4();
}
private static async void GetRepoTry4()
{
try
{
Console.WriteLine("Searching for data"); //returns here... code below is never ran
var searchResults = await Github.Search.SearchRepo(new SearchRepositoriesRequest("octokit"));
if (searchResults != null)
foreach (var result in searchResults.Items)
Console.WriteLine(result.FullName);
Console.WriteLine("Fetching data...."); //testing search
var myrepo = await Github.Repository.Get("Haacked", "octokit.net");
Console.WriteLine("Done! :)");
Console.WriteLine("Repo loaded successfully!");
Console.WriteLine("Repo owner: " + myrepo.Owner);
Console.WriteLine("Repo ID: " + myrepo.Id);
Console.WriteLine("Repo Date: " + myrepo.CreatedAt);
}
catch (Exception e)
{
Console.WriteLine("Ayyyy... troubles"); //never trigged
Console.WriteLine(e.Message);
}
}
}
}
The problem is the await` keyword as it terminates the method and returns.
I'm still learning async methods so it's possible I messed something up, but even my ReSharper says it fine.
I used var to replace task<T> stuff. It seams OK to me plus no warnings nor errors.
I fixed the await issue. Now when I finally connected to GH and tried to get the repo it threw an exeption at both calls to GH (tested with commenting first then second call). e.message was some huge stuff.
I logged it into a file and it looks like an HTML document. Here it is (http://pastebin.com/fxJD1dUb)
Change GetRepoTry4(); to Task.Run(async () => { await GetRepoTry4(); }).Wait(); and private static async void GetRepoTry4() to private static async Task GetRepoTry4().
This should get you at least wired up correctly enough to start debugging the real issue.
Generally speaking all async methods need to return a Task or Task<T> and all methods that return a Task or Task<T> should be async. Additionally, you should get your code into the dispatcher as quickly as possible and start using await.
The constructor with the Uri overload is intended for use with GitHub Enterprise installations, e.g:
static readonly GitHubClient Github = new GitHubClient(new ProductHeaderValue ("Testing123"), new Uri("https://github.enterprise.com/"));
If you're just using it to connect to GitHub, you don't need to specify this:
static readonly GitHubClient Github = new GitHubClient(new ProductHeaderValue ("Testing123"));
You're seeing a HTML page because the base address is incorrect - all of the API-related operations use api.github.com, which is the default.
Install Octokit Nuget Package for Github.Then add below function
public JsonResult GetRepositoryDeatil(long id)
{
var client = new GitHubClient(new ProductHeaderValue("demo"));
var tokenAuth = new Credentials("xxxxxxx"); // NOTE: not real token
client.Credentials = tokenAuth;
var content = client.Repository.Content.GetAllContents(id).Result;
List<RepositoryContent> objRepositoryContentList = content.ToList();
return Json(objRepositoryContentList, JsonRequestBehavior.AllowGet);
}
Due to the use of the async/await you should change the definition of the method GetRepoTry4 to the following:
private static async Task GetRepoTry4()
EDIT:
Then in the Main method call it like so GetRepoTry4().Wait();. This will enable the method GetRepoTry4() to be awaited.

Handling collection events with MongoDB C# driver (v2.0)

Playing with the new MongoDB driver (v2.0) has been quite challenging. Most of the examples you find on the web still refer to the legacy driver. The reference manual for v2.0 on the official Mongo site is "terse", to say the least.
I'm attempting to do a simple thing: detect when a collection has been changed in order to forward a C# event to my server application.
For doing so, I've found the following C# example (see below) that I'm trying to convert to the new API.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;
namespace TestTailableCursor {
public static class Program {
public static void Main(string[] args) {
try {
var server = MongoServer.Create("mongodb://localhost/?safe=true");
var database = server["test"];
if (database.CollectionExists("capped")) {
database.DropCollection("capped");
}
var collectionOptions = CollectionOptions.SetCapped(true).SetMaxDocuments(5).SetMaxSize(10000);
var commandResult = database.CreateCollection("capped", collectionOptions);
var collection = database["capped"];
// to test the tailable cursor manually insert documents into the test.capped collection
// while this program is running and verify that they are echoed to the console window
// see: http://www.mongodb.org/display/DOCS/Tailable+Cursors for C++ version of this loop
BsonValue lastId = BsonMinKey.Value;
while (true) {
var query = Query.GT("_id", lastId);
var cursor = collection.Find(query)
.SetFlags(QueryFlags.TailableCursor | QueryFlags.AwaitData)
.SetSortOrder("$natural");
using (var enumerator = (MongoCursorEnumerator<BsonDocument>) cursor.GetEnumerator()) {
while (true) {
if (enumerator.MoveNext()) {
var document = enumerator.Current;
lastId = document["_id"];
ProcessDocument(document);
} else {
if (enumerator.IsDead) {
break;
}
if (!enumerator.IsServerAwaitCapable) {
Thread.Sleep(TimeSpan.FromMilliseconds(100));
}
}
}
}
}
} catch (Exception ex) {
Console.WriteLine("Unhandled exception:");
Console.WriteLine(ex);
}
Console.WriteLine("Press Enter to continue");
Console.ReadLine();
}
private static void ProcessDocument(BsonDocument document)
{
Console.WriteLine(document.ToJson());
}
}
}
A few (related) questions:
Is that the right approach with the new driver?
If so, how do I set collection options (like SetCap in the example above). The new API includes something called "CollectionSettings", which seems totally
unrelated.
Is my only option to rely on the legacy driver?
Thanks for your help.
Is my only option to rely on the legacy driver?
No.
[...] how do I set collection options (like SetCap in the example above). The new API includes something called "CollectionSettings", which seems totally unrelated.
There's CreateCollectionSettings now. CollectionSettings is a setting for the driver, i.e. a way to specify default behavior per-collection. CreateCollectionOptions can be used like this:
db.CreateCollectionAsync("capped", new CreateCollectionOptions
{ Capped = true, MaxDocuments = 5, MaxSize = 10000 }).Wait();
Is that the right approach with the new driver?
I think so, tailable cursors are a feature of the database, and avoiding polling always makes sense.
I converted the gist of the code and it appears to work on my machineā„¢:
Be careful when using .Result and .Wait() in a web or UI application.
private static void ProcessDocument<T>(T document)where T : class
{
Console.WriteLine(document.ToJson());
}
static async Task Watch<T>(IMongoCollection<T> collection) where T: class
{
try {
BsonValue lastId = BsonMinKey.Value;
while (true) {
var query = Builders<T>.Filter.Gt("_id", lastId);
using (var cursor = await collection.FindAsync(query, new FindOptions<T> {
CursorType = CursorType.TailableAwait,
Sort = Builders<T>.Sort.Ascending("$natural") }))
{
while (await cursor.MoveNextAsync())
{
var batch = cursor.Current;
foreach (var document in batch)
{
lastId = document.ToBsonDocument()["_id"];
ProcessDocument(document);
}
}
}
}
}
catch (Exception ex) {
Console.WriteLine("Unhandled exception:");
Console.WriteLine(ex);
}
}

Categories