I am writing a data access layer which is tested against multiple database implementations; some SQL (Postgres, Sqlite, SQL Server) and some document (MongoDB). In order to test different databases, my test fixtures include a test entity, entity configuration and DbContext
.
For example:
TestEntity
:
public sealed class TestEntity(Guid id, string text, int number, DateTime created)
{
public TestEntity(string text, int number) : this(Guid.Empty, text, number, DateTime.UtcNow)
{
}
public Guid Id { get; } = id;
public string Text { get; } = text;
public int Number { get; } = number;
public DateTime Created { get; } = created;
public Optional<DateTime> Updated { get; private init; } = Optional<DateTime>.None;
public bool IsUpdated => Updated.HasValue;
public TestEntity Update() => new(Id, Text, Number, Created) { Updated = DateTime.UtcNow };
public override string ToString() => this.ToRecordString();
}
TestEntityTypeConfiguration
:
public sealed class TestEntityTypeConfiguration : IEntityTypeConfiguration<TestEntity>
{
public void Configure(EntityTypeBuilder<TestEntity> builder)
{
builder.HasKey(entity => entity.Id);
builder.Property(entity => entity.Id).ValueGeneratedOnAdd();
builder.Property(entity => entity.Text).IsRequired().HasMaxLength(32);
builder.Property(entity => entity.Number).IsRequired();
builder.Property(entity => entity.Created).IsRequired();
builder.Property(entity => entity.Updated).HasConversion<OptionalValueConverter<DateTime>>();
builder.Ignore(entity => entity.IsUpdated);
}
}
TestDbContext
:
public sealed class TestDbContext(DbContextOptions<TestDbContext> options) : DbContext(options)
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new TestEntityTypeConfiguration());
}
}
Note that the integration tests work for all SQL databases. Only MongoDB is failing with the following error:
The entity type ‘TestEntity’ primary key property ‘Id’ must be mapped to element ‘_id’
Given that I am testing against multiple database implementations, I cannot modify TestEntity
, TestEntityTypeConfiguration
, or TestDbContext
without good reason, as I do not want the changes to impact integration tests currently working against SQL databases.
The MongoDB test class is as follows:
IsolatedRepositoryMongoDbIntegrationTests
:
public sealed class IsolatedRepositoryMongoDbIntegrationTests(ITestOutputHelper output) : IsolatedRepositoryIntegrationTests(output)
{
private readonly MongoDbContainer container = new MongoDbBuilder()
.WithUsername("test_user")
.WithPassword("test_password")
.WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(27017))
.Build();
protected override void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TestDbContext>(ConfigureDatabase);
services.AddSingleton<IIsolatedRepository<Guid, TestEntity>, TestIsolatedRepository>();
}
private void ConfigureDatabase(DbContextOptionsBuilder builder)
{
IMongoClient client = new MongoClient(container.GetConnectionString());
builder.UseMongoDB(client, "test_db");
}
public override async Task InitializeAsync()
{
await container.StartAsync();
}
public override async Task DisposeAsync()
{
await container.StopAsync();
await container.DisposeAsync();
}
}
Note that IsolatedRepositoryMongoDbIntegrationTests
inherits the actual test cases from IsolatedRepositoryIntegrationTests
, therefore this class only needs to contain the configuration for MongoDB.
Have I missed some configuration somewhere, or is there some configuration I should add to IsolatedRepositoryMongoDbIntegrationTests
in order to make the tests pass?
I have searched for the error but haven’t found any documentation relating directly to MongoDB and Entity Framework Core, so if you know of anything I should read, please let me know.
2
Answers
I managed to get it working* by making a couple of changes.
Since
TestEntity
andTestDbContext
have no awareness of MongoDB (they are in a higher level dependency), I had to unsealTestDbContext
, so that I could inherit from it.Once I had done that, I could proceed to create a derived
MongoTestDbContext
which derives its configuration fromTestDbContext
, but includes the configuration to map theId
property with an_id
element name:However, this was not enough. The second issue I ran into was that MongoDB could not map via the
TestEntity
constructor, therefore mutable fields requireprivate set
:Finally, I needed to make some DI configuration changes, since the existing DI requires a
TestDbContext
type, therefore this needs to map toMongoTestDbContext
as above:I say got it working because these changes are not optimal:
sealed
is now open for extension, which I don't really want.private set
on the entity properties are superfluous, when Entity Framework is (mostly) capable of mapping via constructors.However, this is sufficient for an integration test.
it looks like you can’t use Entity Framework configuration for Mongo.
Mongo has its own entity map configuration flow
https://www.mongodb.com/docs/drivers/csharp/current/fundamentals/serialization/class-mapping/
https://www.mongodb.com/docs/drivers/csharp/current/fundamentals/serialization/poco/#std-label-csharp-poco