Skip to content

Commit 1f686b7

Browse files
Feat: EntityFramework Snapshots (#108)
* bugfix: don't publish transaction if it wasn't committed to the database * feat: EntityFramework snapshots * test: EntityFramework snapshots * fix: remove test code * refactor: provide a dbContext which handles the boilerplate * fix(test): update tests to match new structure * refactor: use ValueConverter for the ValueObjects * refactor: make choosing table names mandatory * refactor: use ValueObjects instead of Raw Values to keep usage consistent * refactor: don't require a named DbSet property prevents using one DbContext for multiple snapshots (and therefore prevents related snapshots) * refactor: use real transactions for entity framework snapshots in test mode * refactor: update snapshot reference if one already exists * chore: coverage for TimeStamp converter * chore: exclude ToString overrides from coverage * chore: exclude functioning CommitTransaction from tests Tests should be running in TestMode, and should not actually commit data. * chore: remove unused method * fix: swap order of methods * fix: working delete method * Update global.json * chore: include debug logs * refactor: SnapshotReference _has one_ Snapshot instead of _owns_ snapshot * nit: explicit OnDelete behavior * fix: IEntityFrameworkSnapshot belongs in its own file, and in the Snapshots namespace * refactor: better snapshot cleanup 1. delete snapshot when all references deleted 2. delete snapshot when all references are changed to point to a different snapshot 3. provide an option to opt-out of cleanup behavior * refactor: force a constructor instead of using IDbContextFactory this allows for a single DbContext to have multiple connection configurations (e.g., read-only connection string) * fix: somehow this wasn't on this branch originally * refactor: rename bits and bobs IEntityDbContext, EntityDbContextBase does not explicitly depend on involving snapshots * refactor: IEntityDbContextFactory allow the end user to create DbContexts using just session option names * Empty Commit
1 parent b8cddec commit 1f686b7

File tree

45 files changed

+1670
-157
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1670
-157
lines changed

EntityDb.sln

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.SqlDb", "src\Entit
4747
EndProject
4848
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Json", "src\EntityDb.Json\EntityDb.Json.csproj", "{4936FFE0-98E5-43A2-89C9-0415A13CAA9B}"
4949
EndProject
50-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EntityDb.Provisioner", "src\EntityDb.Provisioner\EntityDb.Provisioner.csproj", "{26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}"
50+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.Provisioner", "src\EntityDb.Provisioner\EntityDb.Provisioner.csproj", "{26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}"
51+
EndProject
52+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EntityDb.EntityFramework", "src\EntityDb.EntityFramework\EntityDb.EntityFramework.csproj", "{199606BF-6283-4684-A224-4DA7E80D8F45}"
5153
EndProject
5254
Global
5355
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -115,6 +117,10 @@ Global
115117
{26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
116118
{26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
117119
{26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3}.Release|Any CPU.Build.0 = Release|Any CPU
120+
{199606BF-6283-4684-A224-4DA7E80D8F45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
121+
{199606BF-6283-4684-A224-4DA7E80D8F45}.Debug|Any CPU.Build.0 = Debug|Any CPU
122+
{199606BF-6283-4684-A224-4DA7E80D8F45}.Release|Any CPU.ActiveCfg = Release|Any CPU
123+
{199606BF-6283-4684-A224-4DA7E80D8F45}.Release|Any CPU.Build.0 = Release|Any CPU
118124
EndGlobalSection
119125
GlobalSection(SolutionProperties) = preSolution
120126
HideSolutionNode = FALSE
@@ -135,6 +141,7 @@ Global
135141
{F2491666-31D1-47B5-A493-F25E167D1FDF} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960}
136142
{4936FFE0-98E5-43A2-89C9-0415A13CAA9B} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960}
137143
{26FCDB9D-0DE3-4BB9-858D-3E2C3EF763E3} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960}
144+
{199606BF-6283-4684-A224-4DA7E80D8F45} = {ABACFBCC-B59F-4616-B6CC-99C37AEC8960}
138145
EndGlobalSection
139146
GlobalSection(ExtensibilityGlobals) = postSolution
140147
SolutionGuid = {E9D288EE-9351-4018-ABE8-B0968AEB0465}

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sdk": {
3-
"version": "7.0.100",
3+
"version": "7.0.201",
44
"allowPrerelease": false,
55
"rollForward": "disable"
66
}

src/EntityDb.Common/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// src
44
[assembly: InternalsVisibleTo("EntityDb.SqlDb")]
55
[assembly: InternalsVisibleTo("EntityDb.Npgsql")]
6+
[assembly: InternalsVisibleTo("EntityDb.EntityFramework")]
67
[assembly: InternalsVisibleTo("EntityDb.InMemory")]
78
[assembly: InternalsVisibleTo("EntityDb.MongoDb")]
89
[assembly: InternalsVisibleTo("EntityDb.Provisioner")]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using EntityDb.Abstractions.ValueObjects;
2+
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
3+
using System.Linq.Expressions;
4+
5+
namespace EntityDb.EntityFramework.Converters;
6+
7+
internal class IdConverter : ValueConverter<Id, Guid>
8+
{
9+
private static readonly Expression<Func<Id, Guid>> IdToGuid = (id) => id.Value;
10+
private static readonly Expression<Func<Guid, Id>> GuidToId = (guid) => new Id(guid);
11+
12+
public IdConverter() : base(IdToGuid, GuidToId, null)
13+
{
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using EntityDb.Abstractions.ValueObjects;
2+
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
3+
using System.Linq.Expressions;
4+
5+
namespace EntityDb.EntityFramework.Converters;
6+
7+
internal class TimeStampConverter : ValueConverter<TimeStamp, DateTime>
8+
{
9+
private static readonly Expression<Func<TimeStamp, DateTime>> TimeStampToDateTime = (timeStamp) => timeStamp.Value;
10+
private static readonly Expression<Func<DateTime, TimeStamp>> DateTimeToTimeStamp = (dateTime) => new TimeStamp(dateTime);
11+
12+
public TimeStampConverter() : base(TimeStampToDateTime, DateTimeToTimeStamp, null)
13+
{
14+
}
15+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using EntityDb.Abstractions.ValueObjects;
2+
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
3+
using System.Linq.Expressions;
4+
5+
namespace EntityDb.EntityFramework.Converters;
6+
7+
internal class VersionNumberConverter : ValueConverter<VersionNumber, ulong>
8+
{
9+
private static readonly Expression<Func<VersionNumber, ulong>> VersionNumberToUlong = (versionNumber) => versionNumber.Value;
10+
private static readonly Expression<Func<ulong, VersionNumber>> UlongToVersionNumber = (@ulong) => new VersionNumber(@ulong);
11+
12+
public VersionNumberConverter() : base(VersionNumberToUlong, UlongToVersionNumber, null)
13+
{
14+
}
15+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using EntityDb.Abstractions.ValueObjects;
2+
using EntityDb.EntityFramework.Converters;
3+
using Microsoft.EntityFrameworkCore;
4+
5+
namespace EntityDb.EntityFramework.DbContexts;
6+
7+
/// <summary>
8+
/// A DbContext that adds basic converters for types defined in <see cref="Abstractions.ValueObjects"/>
9+
/// </summary>
10+
public abstract class EntityDbContextBase : DbContext
11+
{
12+
/// <inheritdoc />
13+
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
14+
{
15+
configurationBuilder
16+
.Properties<Id>()
17+
.HaveConversion<IdConverter>();
18+
19+
configurationBuilder
20+
.Properties<VersionNumber>()
21+
.HaveConversion<VersionNumberConverter>();
22+
23+
configurationBuilder
24+
.Properties<TimeStamp>()
25+
.HaveConversion<TimeStampConverter>();
26+
}
27+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using EntityDb.EntityFramework.Sessions;
2+
using Microsoft.EntityFrameworkCore;
3+
using Microsoft.Extensions.Options;
4+
5+
namespace EntityDb.EntityFramework.DbContexts;
6+
7+
internal class EntityDbContextFactory<TDbContext> : IEntityDbContextFactory<TDbContext>
8+
where TDbContext : DbContext, IEntityDbContext<TDbContext>
9+
{
10+
private readonly IOptionsFactory<EntityFrameworkSnapshotSessionOptions> _optionsFactory;
11+
12+
public EntityDbContextFactory(IOptionsFactory<EntityFrameworkSnapshotSessionOptions> optionsFactory)
13+
{
14+
_optionsFactory = optionsFactory;
15+
}
16+
17+
public TDbContext Create(string snapshotSessionOptionsName)
18+
{
19+
return TDbContext.Construct(_optionsFactory.Create(snapshotSessionOptionsName));
20+
}
21+
22+
TDbContext IEntityDbContextFactory<TDbContext>.Create(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions)
23+
{
24+
return TDbContext.Construct(snapshotSessionOptions);
25+
}
26+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using EntityDb.EntityFramework.Sessions;
2+
using Microsoft.EntityFrameworkCore;
3+
4+
namespace EntityDb.EntityFramework.DbContexts;
5+
6+
/// <summary>
7+
/// A type of a <see cref="DbContext"/> that can be used for EntityDb purposes.
8+
/// </summary>
9+
/// <typeparam name="TDbContext">The type of the <see cref="DbContext"/></typeparam>
10+
public interface IEntityDbContext<TDbContext>
11+
where TDbContext : DbContext, IEntityDbContext<TDbContext>
12+
{
13+
/// <summary>
14+
/// Returns a new <typeparamref name="TDbContext"/> that will be configured using <paramref name="entityFrameworkSnapshotSessionOptions"/>.
15+
/// </summary>
16+
/// <param name="entityFrameworkSnapshotSessionOptions">The options for the database</param>
17+
/// <returns>A new <typeparamref name="TDbContext"/> that will be configured using <paramref name="entityFrameworkSnapshotSessionOptions"/>.</returns>
18+
static abstract TDbContext Construct(EntityFrameworkSnapshotSessionOptions entityFrameworkSnapshotSessionOptions);
19+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using EntityDb.EntityFramework.Sessions;
2+
using Microsoft.EntityFrameworkCore;
3+
4+
namespace EntityDb.EntityFramework.DbContexts;
5+
6+
/// <summary>
7+
/// Represents a type used to create instances of <typeparamref name="TDbContext"/>.
8+
/// </summary>
9+
/// <typeparam name="TDbContext">The type of the <see cref="DbContext"/>.</typeparam>
10+
public interface IEntityDbContextFactory<TDbContext>
11+
{
12+
internal TDbContext Create(EntityFrameworkSnapshotSessionOptions snapshotSessionOptions);
13+
14+
/// <summary>
15+
/// Create a new instance of <typeparamref name="TDbContext"/>.
16+
/// </summary>
17+
/// <param name="snapshotSessionOptionsName">The agent's use case for the <see cref="DbContext"/>.</param>
18+
/// <returns>A new instance of <typeparamref name="TDbContext"/>.</returns>
19+
TDbContext Create(string snapshotSessionOptionsName);
20+
}

0 commit comments

Comments
 (0)