The instance name is defined as:
if (scopeSuffix is null)
{
return typeof(TDbContext).Name;
}
return $"{typeof(TDbContext).Name}_{scopeSuffix}";That InstanceName is then used to derive the data directory. In order:
- If
LocalDBDataenvironment variable exists then useLocalDBData\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:
var sqlInstance = new SqlInstance(
name: "theInstanceName",
buildTemplate: TestDbBuilder.CreateTable,
directory: @"C:\LocalDb\theInstance");var sqlInstance = new SqlInstance<TheDbContext>(
constructInstance: builder => new(builder.Options),
storage: new("theInstanceName", @"C:\LocalDb\theInstance"));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
}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 thetestFileandmemberNameare 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 = "")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}";
}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)Which can be used as follows:
await using var database = await sqlInstance.Build("TheTestWithDbName");await using var database = await sqlInstance.Build("TheTestWithDbName");