Interactions with SqlLocalDB via Classic Entity Framework.
https://nuget.org/packages/EfClassicLocalDb/
The snippets use a DbContext of the following form:
using System.Data.Entity;
public class TheDbContext(DbConnection connection) :
DbContext(connection, false)
{
public DbSet<TheEntity> TestEntities { get; set; } = null!;
protected override void OnModelCreating(DbModelBuilder model) => model.Entity<TheEntity>();
}
public class TheEntity
{
public int Id { get; set; }
public string? Property { get; set; }
}
SqlInstance needs to be initialized once.
To ensure this happens only once there are several approaches that can be used:
In the static constructor of a test.
If all tests that need to use the SqlInstance existing in the same test class, then the SqlInstance can be initialized in the static constructor of that test class.
public class Tests
{
static SqlInstance<TheDbContext> sqlInstance;
static Tests() =>
sqlInstance = new(
connection => new(connection));
public async Task Test()
{
var entity = new TheEntity
{
Property = "prop"
};
var data = new List<object>
{
entity
};
using var database = await sqlInstance.Build(data);
Assert.Single(database.Context.TestEntities);
}
}
If multiple tests need to use the SqlInstance, then the SqlInstance should be initialized in the static constructor of test base class.
public abstract class TestBase
{
static SqlInstance<TheDbContext> sqlInstance;
static TestBase() =>
sqlInstance = new(
constructInstance: connection => new(connection));
public Task<SqlDatabase<TheDbContext>> LocalDb(
[CallerFilePath] string testFile = "",
string? databaseSuffix = null,
[CallerMemberName] string memberName = "") =>
sqlInstance.Build(testFile, databaseSuffix, memberName);
}
public class Tests :
TestBase
{
[Fact]
public async Task Test()
{
using var database = await LocalDb();
var entity = new TheEntity
{
Property = "prop"
};
await database.AddData(entity);
Assert.Single(database.Context.TestEntities);
}
}
Data can be seeded into the template database for use across all tests:
public class BuildTemplate
{
static SqlInstance<BuildTemplateDbContext> sqlInstance;
static BuildTemplate() =>
sqlInstance = new(
constructInstance: connection => new(connection),
buildTemplate: async context =>
{
await context.CreateOnExistingDb();
var entity = new TheEntity
{
Property = "prop"
};
context.TestEntities.Add(entity);
await context.SaveChangesAsync();
});
[Fact]
public async Task Test()
{
using var database = await sqlInstance.Build();
Assert.Single(database.Context.TestEntities);
}
}
Usage inside a test consists of two parts:
using var database = await sqlInstance.Build();
using (var data = database.NewDbContext())
{
The above are combined in a full test:
#if(!NETCOREAPP3_1)
using EfLocalDb;
public class EfSnippetTests
{
static SqlInstance<MyDbContext> sqlInstance;
static EfSnippetTests() => sqlInstance = new(connection => new(connection));
[Fact]
public async Task TheTest()
{
using var database = await sqlInstance.Build();
using (var data = database.NewDbContext())
{
var entity = new TheEntity
{
Property = "prop"
};
data.TestEntities.Add(entity);
await data.SaveChangesAsync();
}
using (var data = database.NewDbContext())
{
Assert.Single(data.TestEntities);
}
}
[Fact]
public async Task TheTestWithDbName()
{
using var database = await sqlInstance.Build("TheTestWithDbName");
var entity = new TheEntity
{
Property = "prop"
};
await database.AddData(entity);
Assert.Single(database.Context.TestEntities);
}
}
#endif
It is possible to pass the path to a pre-existing template to SqlInstance. This is useful if the database contains stored procedures, or requires a large amount of test data.
using EfLocalDb;
// ReSharper disable NotAccessedField.Local
static class SuppliedTemplate
{
static SqlInstance<MyDbContext> sqlInstance;
static SuppliedTemplate() =>
sqlInstance = new(
connection => new(connection),
existingTemplate: new("template.mdf", "template_log.ldf"));
}
QuietDbConfiguration
applies the approaches outlined in Reducing Code First Database Chatter.
Add the following to the same Assembly that the DbContext
implementation exists in:
public class DbConfiguration :
QuietDbConfiguration;