Thursday, August 30, 2012

WebDrivers in Parallel

Here is a post that is long over due, and is finally getting published by request!

Concurrent WebDrivers

A WebDriver is not thread safe, but it is not required to be a singleton either. If you instantiate multiple drivers, they can all be run at the same time. If you cast a series of drivers into a collection of the IWebDriver interface, and then throw that into a Parallel ForEach, you have yourself one fun toy to play with!

So what are some uses of this?

Admittedly it is not a particularly common or practical use case to have multiple automated browser sessions running at the same time, but it can still come in handy. One fun application is to test website concurrency. You can do a minor load test of a webpage, or your can have two sessions trying to race each other for a limited resource.

...also, running WebDrivers in parallel can make for a killer demo!

Sample Code (xUnit Test)

public class ParallelDemo : IDisposable
{
    public IList<IWebDriver> Drivers { get; private set; }
 
    public ParallelDemo()
    {
        Drivers = new List<IWebDriver>
        {
            new InternetExplorerDriver(),
            new FirefoxDriver(),
            new ChromeDriver()
        };
    }
 
    public void Dispose()
    {
        foreach (var driver in Drivers)
        {
            driver.Close();
            driver.Dispose();
        }
    }
 
    [Fact]
    public void ParallelSearch()
    {
        Parallel.ForEach(Drivers, SearchForTom);
    }
 
    private static void SearchForTom(IWebDriver driver)
    {
        driver.Url = "https://www.google.com/#q=tom+dupont";
        var firstResult = driver.FindElement(By.CssSelector("h3 > a.l"));
        Assert.Contains("Tom DuPont .NET", firstResult.Text);
    }
}
Shout it

Enjoy,
Tom

Saturday, August 18, 2012

Linq to Sql Batch Future Queries

Batch queries are crucial feature of any good ORM...including LinqToSql!

Future queries are an unobtrusive and easy to use way of batching database queries into a lazy loaded data structures. Future queries were originally a feature of NHibernate, and have since been ported to other ORMs. However I am currently working on a project where I do not have access to those tools.

Thus I created LinqToSql.Futures, a simple extension that adds batched futures queries to LinqToSql.

Using Future Queries

  1. Get the LinqToSqlFutures NuGet package.
  2. Extend your DataContext to implement IFutureDataContext. (This is optional, see below!)
  3. Use future queries!

To batch queries together, simply call the ToFuture extension method on your IQueryables, this will return a Lazy collection of entities. The first time that one of the Lazy collections is accessed, all of the pending queries will be batched and executed together.

[Fact]
public void ToFuture()
{
    // SimpleDataContext Implements IFutureDataContext 
    using (var dataContext = new SimpleDataContext())
    {
        Lazy<IList<Person>> people = dataContext.Persons
            .Where(p => p.FirstName == "Tom" || p.FirstName == "Cat")
            .ToFuture();
 
        Lazy<IList<Pet>> pets = dataContext.Pets
            .Where(p => p.Name == "Taboo")
            .ToFuture();
 
        // Single database call!
        Assert.Equal(2, people.Value.Count);
        Assert.Equal(1, pets.Value.Count);
    }
}

To see the future queries working, you can use SqlProfiler to capture the batch query:

Extending Your DataContext

To use the code above, you need only extend your generated DataContext to implement the IFutureDataContext interface (which consists of a mere one property). Additionally, the sample code below overrides the Dispose method to optimize query disposal.

partial class SimpleDataContext : IFutureDataContext
{
    protected override void Dispose(bool disposing)
    {
        if (_futureCollection != null)
        {
            _futureCollection.Dispose();
            _futureCollection = null;
        }
 
        base.Dispose(disposing);
    }
 
    private IFutureCollection _futureCollection;
    public IFutureCollection FutureCollection
    {
        get
        {
            if (_futureCollection == null)
                _futureCollection = this.CreateFutureCollection();
 
            return _futureCollection;
        }
    }
}

Do you not have access to extend your DataContext? No problem...

Alternative Consumption

Your DataContext does not need to implement IFutureDataContext to use future queries. You can also create a FutureCollection and pass that into the ToFuture method. This will provide the future queries with a batch context.

[Fact]
public void FutureCollection()
{
    using (var dataContext = new SimpleDataContext())
    using (var futureCollcetion = dataContext.CreateFutureCollection())
    {
        Lazy<IList<Person>> people = dataContext.Persons
            .Where(p => p.FirstName == "Tom" || p.FirstName == "Cat")
            .ToFuture(futureCollcetion);
 
        Lazy<IList<Pet>> pets = dataContext.Pets
            .Where(p => p.Name == "Taboo")
            .ToFuture(futureCollcetion);
 
        // Single database call!
        Assert.Equal(2, people.Value.Count);
        Assert.Equal(1, pets.Value.Count);
    }
}

Links

If you enjoy the future queries or need to extend them, then please feel free to download the source from GitHub.

LinqToSql.Futures on GitHub
LinqToSql.Futures on NuGet

Shout it

Enjoy,
Tom

Real Time Web Analytics