Skip to content

Latest commit

 

History

History
167 lines (130 loc) · 6.09 KB

directory-and-name-resolution.md

File metadata and controls

167 lines (130 loc) · 6.09 KB

SqlLocalDB Directory and Instance Name Resolution

The instance name is defined as:

if (scopeSuffix == null)
{
    return typeof(TDbContext).Name;
}

return $"{typeof(TDbContext).Name}_{scopeSuffix}";

snippet source / anchor

That InstanceName is then used to derive the data directory. In order:

  • If LocalDBData environment variable exists then use LocalDBData\InstanceName.
  • Use %TempDir%\LocalDb\InstanceName.

There is an explicit registration override that takes an instance name and a directory for that instance:

For SQL:

var sqlInstance = new SqlInstance(
    name: "theInstanceName",
    buildTemplate: TestDbBuilder.CreateTable,
    directory: @"C:\LocalDb\theInstance"
);

snippet source / anchor

For EntityFramework:

var sqlInstance = new SqlInstance<TheDbContext>(
    constructInstance: builder => new TheDbContext(builder.Options),
    name: "theInstanceName",
    directory: @"C:\LocalDb\theInstance");

snippet source / anchor

Building using Azure machines

When using azure hosted machines for build agents, it makes sense to use the agent temp directory as defined by the AGENT_TEMPDIRECTORY environment variable. The reason is that the temp directory is located on a secondary drive. However this drive has some strange permissions that will cause run time errors, usually manifesting as a SqlException with Could not open new database.... To work around this run the following script at machine startup:

$paths = @('D:\Agent', 'D:\Agent\Work', 'D:\Agent\Work\_Temp')

$paths | % {
    $d = [System.IO.Directory]::CreateDirectory($_)
    $acl = Get-Acl $d.FullName
    $ar = new-object System.Security.AccessControl.FileSystemAccessRule("Everyone", "FullControl", "ContainerInherit, ObjectInherit", "None", "Allow")
    $acl.AddAccessRule($ar)
    Set-Acl $d.FullName -AclObject $acl
}

snippet source / anchor

Database Name Resolution

A design goal is to have an isolated database per test. To facilitate this the SqlInstance.Build method has a convention based approach. It contains the following parameters:

  • testFile: defaults to the full path of the source file that contains the caller via CallerFilePathAttribute.
  • memberName: defaults to the method name of the caller to the method via CallerMemberName.
  • databaseSuffix: an optional parameter to further uniquely a database name when the testFile and memberName are not sufficient. For example when using parameterized tests.

The convention signature is as follows:

/// <summary>
///   Build database with a name based on the calling Method.
/// </summary>
/// <param name="testFile">The path to the test class. Used to make the database name unique per test type.</param>
/// <param name="databaseSuffix">For Xunit theories add some text based on the inline data to make the db name unique.</param>
/// <param name="memberName">Used to make the db name unique per method. Will default to the caller method name is used.</param>
public Task<SqlDatabase> Build(
    [CallerFilePath] string testFile = "",
    string? databaseSuffix = null,
    [CallerMemberName] string memberName = "")

snippet source / anchor

With these parameters the database name is the derived as follows:

public static string DeriveDbName(string? databaseSuffix, string memberName, string testClass)
{
    if (databaseSuffix == null)
    {
        return $"{testClass}_{memberName}";
    }
    return $"{testClass}_{memberName}_{databaseSuffix}";
}

snippet source / anchor

Explicit name

If full control over the database name is required, there is an overload that takes an explicit name:

/// <summary>
///   Build database with an explicit name.
/// </summary>
public async Task<SqlDatabase> Build(string dbName)

snippet source / anchor

Which can be used as follows:

For SQL:

using (var database = await sqlInstance.Build("TheTestWithDbName"))
{

snippet source / anchor

For EntityFramework:

using (var database = await sqlInstance.Build("TheTestWithDbName"))
{

snippet source / anchor