Skip to content

Latest commit

 

History

History
178 lines (139 loc) · 6.86 KB

directory-and-name-resolution.md

File metadata and controls

178 lines (139 loc) · 6.86 KB

SqlLocalDB Directory and Instance Name Resolution

The instance name is defined as:

if (scopeSuffix is 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 %Temp%\LocalDb\InstanceName.

Database files older than a day will be purged from the data directory.

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(builder.Options),
    storage: new("theInstanceName", @"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 for this 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? suffix,
    string member,
    string testClass)
{
    if (suffix is null)
    {
        return $"{testClass}_{member}";
    }

    return $"{testClass}_{member}_{suffix}";
}

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:

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

snippet source | anchor

For EntityFramework:

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

snippet source | anchor