0

It seems my DI container makes a new instance for ChromeDriver (IWebDriver) each time I try and get it from the container? All of this happened after refactoring my code. I suddenly needed Selenium by reference for the methods below, otherwise, it wouldn't update the DOM throughout new page loads as I was passing it by value.

Here are the original methods before refactoring,

public static bool ElementExists(IWebDriver selenium, By selector)
{
    try
    {
        selenium.FindElement(selector);
        return true;
    }
    catch (NoSuchElementException)
    {
        return false;
    }
}

public static void WaitForElements(IWebDriver selenium, List<By> selectors, string name = "")
{
    new ConsoleLogger().Trace("Waiting for " + (string.IsNullOrEmpty(name) ? selectors.Count + " items" : name) + ", give us a second.");

    while (selectors.Where(x => ElementExists(selenium, x)).Count() < selectors.Count)
    {
        Thread.Sleep(100);
    }
}

I thought hmm, this is going to be a tricky one. I needed some sort of static instance that I could always pass by reference, I refactored it to this.

public static bool ElementExists(By selector)
{
    var selenium = Reusables.GetServiceProvider().GetService<IWebDriver>();

    try
    {
        selenium.FindElement(selector);
        return true;
    }
    catch (NoSuchElementException)
    {
        return false;
    }
}

Reusables class:

public static class Reusables
{
    public static IDependencyProvider DependencyProvider;

    public static IServiceProvider GetServiceProvider()
    {
        return DependencyProvider.BuildServiceProvider();
    }
}

Program:

private static void Main(string[] args)
{
    var diProvider = new DependencyProvider();
    Reusables.DependencyProvider = diProvider;

    Console.ForegroundColor = ConsoleColor.White;

    Console.CancelKeyPress += (sender, eArgs) => {
        QuitEvent.Set();
        eArgs.Cancel = true;
    };

    Console.WriteLine();
    Console.CursorVisible = false;

    /*var config = serviceProvider.GetService<IConfigProvider>();
    config.Load("https://kskdkskd.kdskdkk", new WebClient());*/

    var scraper = Reusables.GetServiceProvider().GetService<IScraperHandler>();

    scraper.Start();

    QuitEvent.WaitOne();
}

Not sure if its needed, but here's how I register my dependencies:

public class DependencyProvider : ServiceCollection, IDependencyProvider
{
    public DependencyProvider()
    {
        Register();
    }

    public void Register()
    {
        this.AddSingleton<IAuthProvider, AuthProvider>();

        var options = new ChromeOptions();

        options.AddArguments("--disable-notifications");
        options.SetLoggingPreference(LogType.Browser, LogLevel.Off);

        this.AddSingleton<IWebDriver>(provider =>
            new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), options)
        );

        var links = File.ReadAllLines("***");

        this.AddSingleton<IHttpHandler, HttpHandler>();
        this.AddSingleton<IEnumerable>(stack => new Stack<string>(links.ToList()));
        this.AddSingleton<IScraperHandler, ScraperHandler>();
        this.AddSingleton<IConfigProvider, JsonConfigProvider>();
    }
}
dymanoid
  • 14,771
  • 4
  • 36
  • 64
AAA
  • 361
  • 1
  • 5
  • 19
  • You are building a new service provider every time `Reusables.GetServiceProvider` is invoked. which will result in a new instance every time it gets a service. – Nkosi Sep 26 '19 at 21:12

1 Answers1

0

You are building a new service provider every time Reusables.GetServiceProvider is invoked. Which will result in a new provider each time, resulting in a new instance every time the new service provider gets a service.

If the goal is to have a single provider then a singleton is required.

public static class Reusables {
    public static IDependencyProvider DependencyProvider;

    private static Lazy<IServiceProvider> serviceProvider = 
        new Lazy<IServiceProvider>(() => DependencyProvider.BuildServiceProvider());

    public static IServiceProvider GetServiceProvider() {
        return serviceProvider.Value;
    }
}

Not a big fan of the above design but it should work.

Nkosi
  • 235,767
  • 35
  • 427
  • 472