diff --git a/src/EntityDb.Abstractions/Entities/IEntityRepository.cs b/src/EntityDb.Abstractions/Entities/IEntityRepository.cs
index 8859d888..1f8251e4 100644
--- a/src/EntityDb.Abstractions/Entities/IEntityRepository.cs
+++ b/src/EntityDb.Abstractions/Entities/IEntityRepository.cs
@@ -1,5 +1,4 @@
-using EntityDb.Abstractions.Snapshots;
-using EntityDb.Abstractions.Transactions;
+using EntityDb.Abstractions.Transactions;
using System;
using System.Threading.Tasks;
@@ -11,24 +10,25 @@ namespace EntityDb.Abstractions.Entities
/// The type of the entity.
public interface IEntityRepository : IDisposable, IAsyncDisposable
{
- ///
- ITransactionRepository TransactionRepository { get; }
-
- ///
- ISnapshotRepository? SnapshotRepository { get; }
-
///
- /// Returns a snapshot or default().
+ /// Returns the most recent snapshot of a or default().
+ ///
+ /// The id of the entity.
+ /// The most recent snapshot of a or constructs a new .
+ Task GetSnapshotOrDefault(Guid entityId);
+
+ ///
+ /// Returns the current state of a or constructs a new .
///
/// The id of the entity.
- /// A snapshot or default().
- Task Get(Guid entityId);
+ /// The current state of a or constructs a new .
+ Task GetCurrentOrConstruct(Guid entityId);
///
/// Inserts a single transaction with an atomic commit.
///
/// The transaction.
- /// true if the insert suceeded, or false if the insert failed.
- Task Put(ITransaction transaction);
+ /// true if the insert succeeded, or false if the insert failed.
+ Task PutTransaction(ITransaction transaction);
}
}
diff --git a/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs b/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs
new file mode 100644
index 00000000..447e6b65
--- /dev/null
+++ b/src/EntityDb.Abstractions/Entities/IEntityRepositoryFactory.cs
@@ -0,0 +1,12 @@
+using EntityDb.Abstractions.Snapshots;
+using EntityDb.Abstractions.Transactions;
+using System.Threading.Tasks;
+
+namespace EntityDb.Abstractions.Entities
+{
+ public interface IEntityRepositoryFactory
+ {
+ Task> CreateRepository(ITransactionSessionOptions transactionSessionOptions,
+ ISnapshotSessionOptions? snapshotSessionOptions = null);
+ }
+}
diff --git a/src/EntityDb.Abstractions/Queries/Filters/ICommandFilter.cs b/src/EntityDb.Abstractions/Queries/Filters/ICommandFilter.cs
new file mode 100644
index 00000000..8c294148
--- /dev/null
+++ b/src/EntityDb.Abstractions/Queries/Filters/ICommandFilter.cs
@@ -0,0 +1,18 @@
+using EntityDb.Abstractions.Queries.FilterBuilders;
+
+namespace EntityDb.Abstractions.Queries.Filters
+{
+ ///
+ /// Represents a type that supplies filtering for a .
+ ///
+ public interface ICommandFilter
+ {
+ ///
+ /// Returns a built from a command filter builder.
+ ///
+ /// The type of filter used by the repository.
+ /// The command filter builder.
+ /// A built from .
+ TFilter GetFilter(ICommandFilterBuilder builder);
+ }
+}
diff --git a/src/EntityDb.Abstractions/Queries/Filters/IFactFilter.cs b/src/EntityDb.Abstractions/Queries/Filters/IFactFilter.cs
new file mode 100644
index 00000000..66cbd82e
--- /dev/null
+++ b/src/EntityDb.Abstractions/Queries/Filters/IFactFilter.cs
@@ -0,0 +1,18 @@
+using EntityDb.Abstractions.Queries.FilterBuilders;
+
+namespace EntityDb.Abstractions.Queries.Filters
+{
+ ///
+ /// Represents a type that supplies filtering for a .
+ ///
+ public interface IFactFilter
+ {
+ ///
+ /// Returns a built from a fact filter builder.
+ ///
+ /// The type of filter used by the repository.
+ /// The fact filter builder.
+ /// A built from .
+ TFilter GetFilter(IFactFilterBuilder builder);
+ }
+}
diff --git a/src/EntityDb.Abstractions/Queries/Filters/ILeaseFilter.cs b/src/EntityDb.Abstractions/Queries/Filters/ILeaseFilter.cs
new file mode 100644
index 00000000..ca48de3d
--- /dev/null
+++ b/src/EntityDb.Abstractions/Queries/Filters/ILeaseFilter.cs
@@ -0,0 +1,18 @@
+using EntityDb.Abstractions.Queries.FilterBuilders;
+
+namespace EntityDb.Abstractions.Queries.Filters
+{
+ ///
+ /// Represents a type that supplies filtering for a .
+ ///
+ public interface ILeaseFilter
+ {
+ ///
+ /// Returns a built from a lease filter builder.
+ ///
+ /// The type of filter used by the repository.
+ /// The lease filter builder.
+ /// A built from .
+ TFilter GetFilter(ILeaseFilterBuilder builder);
+ }
+}
diff --git a/src/EntityDb.Abstractions/Queries/Filters/ISourceFilter.cs b/src/EntityDb.Abstractions/Queries/Filters/ISourceFilter.cs
new file mode 100644
index 00000000..c3aa57c6
--- /dev/null
+++ b/src/EntityDb.Abstractions/Queries/Filters/ISourceFilter.cs
@@ -0,0 +1,18 @@
+using EntityDb.Abstractions.Queries.FilterBuilders;
+
+namespace EntityDb.Abstractions.Queries.Filters
+{
+ ///
+ /// Represents a type that supplies filtering for a .
+ ///
+ public interface ISourceFilter
+ {
+ ///
+ /// Returns a built from a source filter builder.
+ ///
+ /// The type of filter used by the repository.
+ /// The source filter builder.
+ /// A built from .
+ TFilter GetFilter(ISourceFilterBuilder builder);
+ }
+}
diff --git a/src/EntityDb.Abstractions/Queries/Filters/ITagFilter.cs b/src/EntityDb.Abstractions/Queries/Filters/ITagFilter.cs
new file mode 100644
index 00000000..ab019eae
--- /dev/null
+++ b/src/EntityDb.Abstractions/Queries/Filters/ITagFilter.cs
@@ -0,0 +1,18 @@
+using EntityDb.Abstractions.Queries.FilterBuilders;
+
+namespace EntityDb.Abstractions.Queries.Filters
+{
+ ///
+ /// Represents a type that supplies filtering for a .
+ ///
+ public interface ITagFilter
+ {
+ ///
+ /// Returns a built from a tag filter builder.
+ ///
+ /// The type of filter used by the repository.
+ /// The tag filter builder.
+ /// A built from .
+ TFilter GetFilter(ITagFilterBuilder builder);
+ }
+}
diff --git a/src/EntityDb.Abstractions/Queries/ICommandQuery.cs b/src/EntityDb.Abstractions/Queries/ICommandQuery.cs
index 651fbe32..8807a922 100644
--- a/src/EntityDb.Abstractions/Queries/ICommandQuery.cs
+++ b/src/EntityDb.Abstractions/Queries/ICommandQuery.cs
@@ -1,4 +1,4 @@
-using EntityDb.Abstractions.Queries.FilterBuilders;
+using EntityDb.Abstractions.Queries.Filters;
using EntityDb.Abstractions.Queries.SortBuilders;
namespace EntityDb.Abstractions.Queries
@@ -6,16 +6,8 @@ namespace EntityDb.Abstractions.Queries
///
/// Abstracts a query on commands.
///
- public interface ICommandQuery : IQuery
+ public interface ICommandQuery : IQuery, ICommandFilter
{
- ///
- /// Returns a built from a command filter builder.
- ///
- /// The type of filter used by the repository.
- /// The command filter builder.
- /// A built from .
- TFilter GetFilter(ICommandFilterBuilder builder);
-
///
/// Returns a built from a command sort builder.
///
diff --git a/src/EntityDb.Abstractions/Queries/IFactQuery.cs b/src/EntityDb.Abstractions/Queries/IFactQuery.cs
index 09d080d4..d9b3a6c6 100644
--- a/src/EntityDb.Abstractions/Queries/IFactQuery.cs
+++ b/src/EntityDb.Abstractions/Queries/IFactQuery.cs
@@ -1,4 +1,4 @@
-using EntityDb.Abstractions.Queries.FilterBuilders;
+using EntityDb.Abstractions.Queries.Filters;
using EntityDb.Abstractions.Queries.SortBuilders;
namespace EntityDb.Abstractions.Queries
@@ -6,16 +6,8 @@ namespace EntityDb.Abstractions.Queries
///
/// Abstracts a query on facts.
///
- public interface IFactQuery : IQuery
+ public interface IFactQuery : IQuery, IFactFilter
{
- ///
- /// Returns a built from a fact filter builder.
- ///
- /// The type of filter used by the repository.
- /// The fact filter builder.
- /// A built from .
- TFilter GetFilter(IFactFilterBuilder builder);
-
///
/// Returns a built from a fact sort builder.
///
diff --git a/src/EntityDb.Abstractions/Queries/ILeaseQuery.cs b/src/EntityDb.Abstractions/Queries/ILeaseQuery.cs
index 4d32e7c9..46b901cb 100644
--- a/src/EntityDb.Abstractions/Queries/ILeaseQuery.cs
+++ b/src/EntityDb.Abstractions/Queries/ILeaseQuery.cs
@@ -1,4 +1,4 @@
-using EntityDb.Abstractions.Queries.FilterBuilders;
+using EntityDb.Abstractions.Queries.Filters;
using EntityDb.Abstractions.Queries.SortBuilders;
namespace EntityDb.Abstractions.Queries
@@ -6,16 +6,8 @@ namespace EntityDb.Abstractions.Queries
///
/// Abstracts a query on leases.
///
- public interface ILeaseQuery : IQuery
+ public interface ILeaseQuery : IQuery, ILeaseFilter
{
- ///
- /// Returns a built from a lease filter builder.
- ///
- /// The type of filter used by the repository.
- /// The lease filter builder.
- /// A built from .
- TFilter GetFilter(ILeaseFilterBuilder builder);
-
///
/// Returns a built from a lease sort builder.
///
diff --git a/src/EntityDb.Abstractions/Queries/ISourceQuery.cs b/src/EntityDb.Abstractions/Queries/ISourceQuery.cs
index 9edd8f42..cf63aad5 100644
--- a/src/EntityDb.Abstractions/Queries/ISourceQuery.cs
+++ b/src/EntityDb.Abstractions/Queries/ISourceQuery.cs
@@ -1,4 +1,4 @@
-using EntityDb.Abstractions.Queries.FilterBuilders;
+using EntityDb.Abstractions.Queries.Filters;
using EntityDb.Abstractions.Queries.SortBuilders;
namespace EntityDb.Abstractions.Queries
@@ -6,16 +6,8 @@ namespace EntityDb.Abstractions.Queries
///
/// Abstracts a query on sources.
///
- public interface ISourceQuery : IQuery
+ public interface ISourceQuery : IQuery, ISourceFilter
{
- ///
- /// Returns a built from a source filter builder.
- ///
- /// The type of filter used by the repository.
- /// The source filter builder.
- /// A built from .
- TFilter GetFilter(ISourceFilterBuilder builder);
-
///
/// Returns a built from a source sort builder.
///
diff --git a/src/EntityDb.Abstractions/Queries/ITagQuery.cs b/src/EntityDb.Abstractions/Queries/ITagQuery.cs
index c8329c3d..a9be79e2 100644
--- a/src/EntityDb.Abstractions/Queries/ITagQuery.cs
+++ b/src/EntityDb.Abstractions/Queries/ITagQuery.cs
@@ -1,4 +1,4 @@
-using EntityDb.Abstractions.Queries.FilterBuilders;
+using EntityDb.Abstractions.Queries.Filters;
using EntityDb.Abstractions.Queries.SortBuilders;
namespace EntityDb.Abstractions.Queries
@@ -6,16 +6,8 @@ namespace EntityDb.Abstractions.Queries
///
/// Abstracts a query on tags.
///
- public interface ITagQuery : IQuery
+ public interface ITagQuery : IQuery, ITagFilter
{
- ///
- /// Returns a built from a tag filter builder.
- ///
- /// The type of filter used by the repository.
- /// The tag filter builder.
- /// A built from .
- TFilter GetFilter(ITagFilterBuilder builder);
-
///
/// Returns a built from a tag sort builder.
///
diff --git a/src/EntityDb.Abstractions/Strategies/IAuthorizingStrategy.cs b/src/EntityDb.Abstractions/Strategies/IAuthorizingStrategy.cs
index 520feba2..45a0463f 100644
--- a/src/EntityDb.Abstractions/Strategies/IAuthorizingStrategy.cs
+++ b/src/EntityDb.Abstractions/Strategies/IAuthorizingStrategy.cs
@@ -14,8 +14,7 @@ public interface IAuthorizingStrategy
///
/// The entity.
/// The command.
- /// The agent.
/// true if execution is authorized, or false if execution is not authorized.
- bool IsAuthorized(TEntity entity, ICommand command, IAgent agent);
+ bool IsAuthorized(TEntity entity, ICommand command);
}
}
diff --git a/src/EntityDb.Abstractions/Strategies/ISnapshottingStrategy.cs b/src/EntityDb.Abstractions/Strategies/ISnapshottingStrategy.cs
index 6013596f..9c40a01f 100644
--- a/src/EntityDb.Abstractions/Strategies/ISnapshottingStrategy.cs
+++ b/src/EntityDb.Abstractions/Strategies/ISnapshottingStrategy.cs
@@ -4,7 +4,7 @@
/// Represents a type used to determine if the next version of an entity should be put into snapshot storage.
///
/// The type of the entity that can be put into snapshot storage.
- public interface ISnapshottingStrategy
+ public interface ISnapshottingStrategy
{
///
/// Determines if the next version of an entity should be put into snapshot storage.
diff --git a/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs b/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs
index 68cb7399..661c0014 100644
--- a/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs
+++ b/src/EntityDb.Abstractions/Transactions/ITransactionCommand.cs
@@ -12,11 +12,6 @@ namespace EntityDb.Abstractions.Transactions
/// The type of entity to be modified.
public interface ITransactionCommand
{
- ///
- /// A snapshot of the entity before the command.
- ///
- TEntity? PreviousSnapshot { get; }
-
///
/// A snapshot of the entity after the command.
///
diff --git a/src/EntityDb.Abstractions/Transactions/ITransactionSubscriber.cs b/src/EntityDb.Abstractions/Transactions/ITransactionSubscriber.cs
new file mode 100644
index 00000000..5a5ce246
--- /dev/null
+++ b/src/EntityDb.Abstractions/Transactions/ITransactionSubscriber.cs
@@ -0,0 +1,15 @@
+namespace EntityDb.Abstractions.Transactions
+{
+ ///
+ ///
+ ///
+ ///
+ public interface ITransactionSubscriber
+ {
+ ///
+ /// Foo
+ ///
+ ///
+ void Notify(ITransaction transaction);
+ }
+}
diff --git a/src/EntityDb.Common.Tests/Extensions/IServiceProviderExtensionsTests.cs b/src/EntityDb.Common.Tests/Extensions/IServiceProviderExtensionsTests.cs
deleted file mode 100644
index eb7a871b..00000000
--- a/src/EntityDb.Common.Tests/Extensions/IServiceProviderExtensionsTests.cs
+++ /dev/null
@@ -1,64 +0,0 @@
-using EntityDb.Common.Extensions;
-using EntityDb.TestImplementations.Commands;
-using EntityDb.TestImplementations.Entities;
-using Shouldly;
-using System;
-using Xunit;
-
-namespace EntityDb.Common.Tests.Extensions
-{
- public class IServiceProviderExtensionsTests : TestsBase
- {
- public IServiceProviderExtensionsTests(IServiceProvider serviceProvider) : base(serviceProvider)
- {
- }
-
- [Fact]
- public void GivenNoLeasingStrategy_WhenGettingLeases_ThenReturnEmptyArray()
- {
- // ARRANGE
-
- var serviceProvider = GetEmptyServiceProvider();
-
- // ACT
-
- var leases = serviceProvider.GetLeases(new TransactionEntity());
-
- // ASSERT
-
- leases.ShouldBeEmpty();
- }
-
- [Fact]
- public void GivenNoAuthorizingStrategy_ThenIsAuthorizedReturnsTrue()
- {
- // ARRANGE
-
- var serviceProvider = GetEmptyServiceProvider();
-
- // ACT
-
- var isAuthorized = serviceProvider.IsAuthorized(new TransactionEntity(), new DoNothing());
-
- // ASSERT
-
- isAuthorized.ShouldBeTrue();
- }
-
- [Fact]
- public void GivenNoCachingStrategy_WhenCheckingIfShouldCache_ThenReturnFalse()
- {
- // ARRANGE
-
- var serviceProvider = GetEmptyServiceProvider();
-
- // ACT
-
- var shouldCache = serviceProvider.ShouldPutSnapshot(default, default!);
-
- // ASSERT
-
- shouldCache.ShouldBeFalse();
- }
- }
-}
diff --git a/src/EntityDb.Common.Tests/Projections/ProjectionServiceProviderTests.cs b/src/EntityDb.Common.Tests/Projections/ProjectionServiceProviderTests.cs
deleted file mode 100644
index 5a7c969a..00000000
--- a/src/EntityDb.Common.Tests/Projections/ProjectionServiceProviderTests.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace EntityDb.Common.Tests.Projections
-{
- public class ProjectionServiceProviderTests
- {
- }
-}
diff --git a/src/EntityDb.Common.Tests/SnapshotTransactions/SnapshotTransactionsTestsBase.cs b/src/EntityDb.Common.Tests/SnapshotTransactions/SnapshotTransactionsTestsBase.cs
index 693aa4e3..ff983c94 100644
--- a/src/EntityDb.Common.Tests/SnapshotTransactions/SnapshotTransactionsTestsBase.cs
+++ b/src/EntityDb.Common.Tests/SnapshotTransactions/SnapshotTransactionsTestsBase.cs
@@ -1,7 +1,6 @@
using EntityDb.Abstractions.Entities;
using EntityDb.Abstractions.Strategies;
using EntityDb.Abstractions.Transactions;
-using EntityDb.Common.Extensions;
using EntityDb.Common.Snapshots;
using EntityDb.Common.Transactions;
using EntityDb.TestImplementations.Commands;
@@ -25,7 +24,7 @@ protected SnapshotTransactionsTestsBase(IServiceProvider serviceProvider) : base
private static async Task> BuildTransaction(Guid entityId, ulong from, ulong to,
IServiceProvider serviceProvider, IEntityRepository? entityRepository = null)
{
- var transactionBuilder = serviceProvider.GetTransactionBuilder();
+ var transactionBuilder = serviceProvider.GetRequiredService>();
if (entityRepository != null)
{
@@ -46,14 +45,14 @@ private static async Task> BuildTransaction(Guid
[Theory]
[InlineData(10, 20)]
- public async Task GivenCachingOnNthVersion_WhenPuttingTransactionWithNthVersion_ThenSnapshotExistsAtNthVersion(
+ public async Task GivenSnapshottingOnNthVersion_WhenPuttingTransactionWithNthVersion_ThenSnapshotExistsAtNthVersion(
ulong expectedSnapshotVersion, ulong expectedCurrentVersion)
{
// ARRANGE
- var cachingStrategyMock = new Mock>(MockBehavior.Strict);
+ var snapshottingStrategyMock = new Mock>(MockBehavior.Strict);
- cachingStrategyMock
+ snapshottingStrategyMock
.Setup(strategy =>
strategy.ShouldPutSnapshot(It.IsAny(), It.IsAny()))
.Returns((TransactionEntity? _, TransactionEntity nextEntity) =>
@@ -61,29 +60,29 @@ public async Task GivenCachingOnNthVersion_WhenPuttingTransactionWithNthVersion_
var serviceProvider = GetServiceProviderWithOverrides(serviceCollection =>
{
- serviceCollection.AddSingleton(_ => cachingStrategyMock.Object);
+ serviceCollection.AddSingleton(_ => snapshottingStrategyMock.Object);
});
var entityId = Guid.NewGuid();
await using var entityRepository =
- await serviceProvider.CreateEntityRepository(new TransactionSessionOptions(),
+ await serviceProvider.GetRequiredService>().CreateRepository(new TransactionSessionOptions(),
new SnapshotSessionOptions());
var firstTransaction = await BuildTransaction(entityId, 1, expectedSnapshotVersion, serviceProvider);
- await entityRepository.Put(firstTransaction);
+ await entityRepository.PutTransaction(firstTransaction);
var secondTransaction = await BuildTransaction(entityId, expectedSnapshotVersion, expectedCurrentVersion,
serviceProvider, entityRepository);
- await entityRepository.Put(secondTransaction);
+ await entityRepository.PutTransaction(secondTransaction);
// ACT
- var current = await entityRepository.Get(entityId);
+ var current = await entityRepository.GetCurrentOrConstruct(entityId);
- var snapshot = await entityRepository.SnapshotRepository!.GetSnapshot(entityId);
+ var snapshot = await entityRepository.GetSnapshotOrDefault(entityId);
// ASSERT
diff --git a/src/EntityDb.Common.Tests/Snapshots/SnapshotTestsBase.cs b/src/EntityDb.Common.Tests/Snapshots/SnapshotTestsBase.cs
index 7e83520a..8eaa8809 100644
--- a/src/EntityDb.Common.Tests/Snapshots/SnapshotTestsBase.cs
+++ b/src/EntityDb.Common.Tests/Snapshots/SnapshotTestsBase.cs
@@ -1,4 +1,4 @@
-using EntityDb.Common.Extensions;
+using EntityDb.Abstractions.Snapshots;
using EntityDb.Common.Snapshots;
using EntityDb.TestImplementations.Entities;
using Shouldly;
@@ -10,11 +10,11 @@ namespace EntityDb.Common.Tests.Snapshots
{
public abstract class SnapshotTestsBase
{
- private readonly IServiceProvider _serviceProvider;
+ private readonly ISnapshotRepositoryFactory _snapshotRepositoryFactory;
- public SnapshotTestsBase(IServiceProvider serviceProvider)
+ public SnapshotTestsBase(ISnapshotRepositoryFactory snapshotRepositoryFactory)
{
- _serviceProvider = serviceProvider;
+ _snapshotRepositoryFactory = snapshotRepositoryFactory;
}
[Fact]
@@ -27,7 +27,7 @@ public async Task GivenEmptySnapshotRepository_WhenGoingThroughFullCycle_ThenOri
var entityId = Guid.NewGuid();
await using var snapshotRepository =
- await _serviceProvider.CreateSnapshotRepository(new SnapshotSessionOptions());
+ await _snapshotRepositoryFactory.CreateRepository(new SnapshotSessionOptions());
// ACT
diff --git a/src/EntityDb.Common.Tests/Startup.cs b/src/EntityDb.Common.Tests/Startup.cs
index de692f91..94392498 100644
--- a/src/EntityDb.Common.Tests/Startup.cs
+++ b/src/EntityDb.Common.Tests/Startup.cs
@@ -1,6 +1,7 @@
using EntityDb.Common.Extensions;
using EntityDb.TestImplementations.Agents;
using EntityDb.TestImplementations.Entities;
+using EntityDb.TestImplementations.Strategies;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Xunit.DependencyInjection;
@@ -23,8 +24,8 @@ public void ConfigureServices(IServiceCollection serviceCollection)
serviceCollection.AddAgentAccessor();
- serviceCollection.AddConstructingStrategy();
- serviceCollection.AddVersionedEntityVersioningStrategy();
+ serviceCollection.AddEntity();
+
serviceCollection.AddLeasedEntityLeasingStrategy();
serviceCollection.AddAuthorizedEntityAuthorizingStrategy();
}
diff --git a/src/EntityDb.Common.Tests/TestsBase.cs b/src/EntityDb.Common.Tests/TestsBase.cs
index 84e2debc..9eb467be 100644
--- a/src/EntityDb.Common.Tests/TestsBase.cs
+++ b/src/EntityDb.Common.Tests/TestsBase.cs
@@ -2,8 +2,13 @@
using EntityDb.Abstractions.Queries;
using EntityDb.Abstractions.Transactions;
using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.DependencyInjection.Extensions;
using Moq;
using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
using System.Threading.Tasks;
namespace EntityDb.Common.Tests
@@ -17,18 +22,52 @@ public TestsBase(IServiceProvider serviceProvider)
_serviceProvider = serviceProvider;
}
- public IServiceProvider GetEmptyServiceProvider()
+ private IServiceCollection GetParaisteServiceCollection(Type[]? omittedTypes = null)
{
- return new ServiceCollection().BuildServiceProvider();
- }
+ var serviceCollection = new ServiceCollection();
+
+ // Use reflection to get all service descriptors
+
+ var engine = _serviceProvider.GetType()
+ .GetProperties(BindingFlags.Public | BindingFlags.Instance)
+ .Single(x => x.Name == "Engine")
+ .GetValue(_serviceProvider);
+
+ var callSiteFactory = engine!.GetType()
+ .GetProperties(BindingFlags.NonPublic | BindingFlags.Instance)
+ .Single(x => x.Name == "CallSiteFactory")
+ .GetValue(engine);
+
+ var descriptors = (callSiteFactory!.GetType()
+ .GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
+ .Single(x => x.Name == "_descriptors")
+ .GetValue(callSiteFactory) as List)!;
+
+ foreach (var descriptor in descriptors)
+ {
+ if (omittedTypes?.Contains(descriptor.ServiceType) == true)
+ {
+ continue;
+ }
+
+ serviceCollection.Add(descriptor);
+ }
+ return serviceCollection;
+ }
+
public IServiceProvider GetServiceProviderWithOverrides(Action configureOverrides)
{
- var overrideServiceCollection = new ServiceCollection();
+ var serviceCollection = GetParaisteServiceCollection();
- configureOverrides.Invoke(overrideServiceCollection);
+ configureOverrides.Invoke(serviceCollection);
- return new ServiceProviderWithOverrides(overrideServiceCollection.BuildServiceProvider(), _serviceProvider);
+ return serviceCollection.BuildServiceProvider();
+ }
+
+ public IServiceProvider GetServiceProviderWithOmission()
+ {
+ return GetParaisteServiceCollection(new[] { typeof(TOmittedType) }).BuildServiceProvider();
}
public static ITransactionRepositoryFactory GetMockedTransactionRepositoryFactory(
@@ -63,14 +102,5 @@ public static ITransactionRepositoryFactory GetMockedTransactionReposit
return transactionRepositoryFactoryMock.Object;
}
-
- private record ServiceProviderWithOverrides(IServiceProvider OverrideServiceProvider,
- IServiceProvider ServiceProvider) : IServiceProvider
- {
- public object? GetService(Type serviceType)
- {
- return OverrideServiceProvider.GetService(serviceType) ?? ServiceProvider.GetService(serviceType);
- }
- }
}
}
diff --git a/src/EntityDb.Common.Tests/Transactions/TransactionBuilderTests.cs b/src/EntityDb.Common.Tests/Transactions/TransactionBuilderTests.cs
index 020b180e..b391eaaf 100644
--- a/src/EntityDb.Common.Tests/Transactions/TransactionBuilderTests.cs
+++ b/src/EntityDb.Common.Tests/Transactions/TransactionBuilderTests.cs
@@ -1,10 +1,13 @@
using EntityDb.Abstractions.Agents;
using EntityDb.Abstractions.Commands;
+using EntityDb.Abstractions.Entities;
using EntityDb.Abstractions.Facts;
using EntityDb.Abstractions.Strategies;
+using EntityDb.Common.Entities;
using EntityDb.Common.Exceptions;
using EntityDb.Common.Extensions;
using EntityDb.Common.Facts;
+using EntityDb.Common.Transactions;
using EntityDb.TestImplementations.Commands;
using EntityDb.TestImplementations.Entities;
using EntityDb.TestImplementations.Facts;
@@ -22,6 +25,24 @@ public class TransactionBuilderTests : TestsBase
public TransactionBuilderTests(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
+
+ [Fact]
+ public void GivenNoAuthorizingStrategy_WhenExecutingUnauthorizedCommand_ThenBuildSucceeds()
+ {
+ // ARRANGE
+
+ var serviceProvider = GetServiceProviderWithOmission>();
+
+ var transactionBuilder = serviceProvider.GetRequiredService>();
+
+ var entityId = Guid.NewGuid();
+
+ // ACT & ASSERT
+
+ Should.NotThrow(() => transactionBuilder
+ .Create(entityId, new SetRole("Foo"))
+ .Append(entityId, new DoNothing()));
+ }
[Fact]
public async Task GivenExistingEntityId_WhenExecutingUnauthorizedCommand_ThenAppendThrows()
@@ -34,7 +55,7 @@ public async Task GivenExistingEntityId_WhenExecutingUnauthorizedCommand_ThenApp
authorizingStrategyMock
.Setup(strategy => strategy.IsAuthorized(It.IsAny(),
- It.IsAny>(), It.IsAny()))
+ It.IsAny>()))
.Returns(false);
var authorizingStrategy = authorizingStrategyMock.Object;
@@ -50,10 +71,10 @@ public async Task GivenExistingEntityId_WhenExecutingUnauthorizedCommand_ThenApp
serviceCollection.AddScoped(_ => authorizingStrategy);
});
- var transactionBuilder = serviceProvider.GetTransactionBuilder();
+ var transactionBuilder = serviceProvider.GetRequiredService>();
await using var entityRepository =
- await serviceProvider.CreateEntityRepository(default!);
+ await serviceProvider.GetRequiredService>().CreateRepository(default!);
// ACT
@@ -78,7 +99,7 @@ public void GivenNonExistingEntityId_WhenExecutingUnauthorizedCommand_ThenCreate
authorizingStrategyMock
.Setup(strategy => strategy.IsAuthorized(It.IsAny(),
- It.IsAny>(), It.IsAny()))
+ It.IsAny>()))
.Returns(false);
var authorizingStrategy = authorizingStrategyMock.Object;
@@ -91,7 +112,7 @@ public void GivenNonExistingEntityId_WhenExecutingUnauthorizedCommand_ThenCreate
serviceCollection.AddScoped(_ => authorizingStrategy);
});
- var transactionBuilder = serviceProvider.GetTransactionBuilder();
+ var transactionBuilder = serviceProvider.GetRequiredService>();
// ASSERT
@@ -114,7 +135,7 @@ public void GivenNonExistingEntityId_WhenUsingEntityIdForAppend_ThenAppendThrows
GetMockedTransactionRepositoryFactory());
});
- var transactionBuilder = serviceProvider.GetTransactionBuilder();
+ var transactionBuilder = serviceProvider.GetRequiredService>();
// ASSERT
@@ -137,7 +158,7 @@ public void GivenExistingEntityId_WhenUsingEntityIdForCreate_ThenCreateThrows()
GetMockedTransactionRepositoryFactory());
});
- var transactionBuilder = serviceProvider.GetTransactionBuilder();
+ var transactionBuilder = serviceProvider.GetRequiredService>();
// ACT
@@ -151,6 +172,48 @@ public void GivenExistingEntityId_WhenUsingEntityIdForCreate_ThenCreateThrows()
});
}
+ [Fact]
+ public void GivenLeasingStrategy_WhenBuildingNewEntityWithLease_ThenTransactionDoesInsertLeases()
+ {
+ // ARRANGE
+
+ var transactionBuilder = _serviceProvider.GetRequiredService>();
+
+ // ACT
+
+ var transaction = transactionBuilder
+ .Create(default, new AddLease(default!, default!, default!))
+ .Build(default, default!);
+
+ // ASSERT
+
+ transaction.Commands.Length.ShouldBe(1);
+
+ transaction.Commands[0].Leases.Insert.ShouldNotBeEmpty();
+ }
+
+ [Fact]
+ public void GivenNoLeasingStrategy_WhenBuildingNewEntityWithLease_ThenTransactionDoesNotInsertLeases()
+ {
+ // ARRANGE
+
+ var serviceProvider = GetServiceProviderWithOmission>();
+
+ var transactionBuilder = serviceProvider.GetRequiredService>();
+
+ // ACT
+
+ var transaction = transactionBuilder
+ .Create(default, new AddLease(default!, default!, default!))
+ .Build(default, default!);
+
+ // ASSERT
+
+ transaction.Commands.Length.ShouldBe(1);
+
+ transaction.Commands[0].Leases.Insert.ShouldBeEmpty();
+ }
+
[Fact]
public async Task GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadThrows()
{
@@ -165,10 +228,10 @@ public async Task GivenExistingEntityId_WhenUsingEntityIdForLoadTwice_ThenLoadTh
new IFact[] { new VersionNumberSet(1) }));
});
- var transactionBuilder = serviceProvider.GetTransactionBuilder();
+ var transactionBuilder = serviceProvider.GetRequiredService>();
await using var entityRepository =
- await serviceProvider.CreateEntityRepository(default!);
+ await serviceProvider.GetRequiredService>().CreateRepository(default!);
// ACT
@@ -193,10 +256,10 @@ public async Task GivenNonExistentEntityId_WhenLoadingEntity_ThenLoadThrows()
GetMockedTransactionRepositoryFactory());
});
- var transactionBuilder = serviceProvider.GetTransactionBuilder();
+ var transactionBuilder = serviceProvider.GetRequiredService>();
await using var entityRepository =
- await serviceProvider.CreateEntityRepository(default!);
+ await serviceProvider.GetRequiredService>().CreateRepository(default!);
// ASSERT
@@ -221,7 +284,7 @@ public void GivenNonExistingEntityId_WhenUsingValidVersioningStrategy_ThenVersio
GetMockedTransactionRepositoryFactory());
});
- var transactionBuilder = serviceProvider.GetTransactionBuilder();
+ var transactionBuilder = serviceProvider.GetRequiredService>();
// ACT
@@ -258,10 +321,10 @@ public async Task GivenExistingEntity_WhenAppendingNewCommand_ThenTransactionBui
}));
});
- var transactionBuilder = serviceProvider.GetTransactionBuilder();
+ var transactionBuilder = serviceProvider.GetRequiredService>();
await using var entityRepository =
- await serviceProvider.CreateEntityRepository(default!);
+ await serviceProvider.GetRequiredService>().CreateRepository(default!);
// ACT
diff --git a/src/EntityDb.Common.Tests/Transactions/TransactionTestsBase.cs b/src/EntityDb.Common.Tests/Transactions/TransactionTestsBase.cs
index 874e3226..2d270dc1 100644
--- a/src/EntityDb.Common.Tests/Transactions/TransactionTestsBase.cs
+++ b/src/EntityDb.Common.Tests/Transactions/TransactionTestsBase.cs
@@ -3,6 +3,7 @@
using EntityDb.Abstractions.Leases;
using EntityDb.Abstractions.Loggers;
using EntityDb.Abstractions.Queries;
+using EntityDb.Abstractions.Strategies;
using EntityDb.Abstractions.Tags;
using EntityDb.Abstractions.Transactions;
using EntityDb.Common.Entities;
@@ -20,6 +21,7 @@
using EntityDb.TestImplementations.Queries;
using EntityDb.TestImplementations.Source;
using EntityDb.TestImplementations.Tags;
+using Microsoft.Extensions.DependencyInjection;
using Moq;
using Shouldly;
using System;
@@ -43,7 +45,7 @@ protected TransactionTestsBase(IServiceProvider serviceProvider)
private Task> CreateRepository(bool readOnly = false,
bool tolerateLag = false, ILogger? loggerOverride = null)
{
- return _serviceProvider.CreateTransactionRepository(new TransactionSessionOptions
+ return _serviceProvider.GetRequiredService>().CreateRepository(new TransactionSessionOptions
{
ReadOnly = readOnly, SecondaryPreferred = tolerateLag, LoggerOverride = loggerOverride
});
@@ -472,7 +474,7 @@ private Task TestGetTags(ITagQuery query, List>
private ITransaction BuildTransaction(Guid transactionId, Guid entityId, object source,
ICommand[] commands, DateTime? timeStampOverride = null)
{
- var transactionBuilder = _serviceProvider.GetTransactionBuilder();
+ var transactionBuilder = _serviceProvider.GetRequiredService>();
transactionBuilder.Create(entityId, commands[0]);
@@ -507,7 +509,6 @@ public async Task GivenReadOnlyMode_WhenPuttingTransaction_ThenThrow()
{
new TransactionCommand
{
- PreviousSnapshot = default,
NextSnapshot = default!,
EntityId = Guid.NewGuid(),
ExpectedPreviousVersionNumber = 0,
@@ -545,7 +546,6 @@ static ITransaction NewTransaction(Guid transactionId)
{
new TransactionCommand
{
- PreviousSnapshot = default,
NextSnapshot = default!,
EntityId = Guid.NewGuid(),
ExpectedPreviousVersionNumber = 0,
@@ -588,7 +588,6 @@ public async Task GivenNonUniqueVersionNumbers_WhenInsertingCommands_ThenReturnF
{
new TransactionCommand
{
- PreviousSnapshot = default,
NextSnapshot = default!,
EntityId = entityId,
ExpectedPreviousVersionNumber = previousVersionNumber,
@@ -599,7 +598,6 @@ public async Task GivenNonUniqueVersionNumbers_WhenInsertingCommands_ThenReturnF
},
new TransactionCommand
{
- PreviousSnapshot = default,
NextSnapshot = default!,
EntityId = entityId,
ExpectedPreviousVersionNumber = previousVersionNumber,
@@ -643,7 +641,6 @@ static ITransaction NewTransaction(Guid entityId, ulong previ
{
new TransactionCommand
{
- PreviousSnapshot = default,
NextSnapshot = default!,
EntityId = entityId,
ExpectedPreviousVersionNumber = previousVersionNumber,
@@ -696,7 +693,6 @@ public async Task GivenNonUniqueSubversionNumbers_WhenInsertingFacts_ThenReturnF
{
new TransactionCommand
{
- PreviousSnapshot = default,
NextSnapshot = default!,
EntityId = entityId,
ExpectedPreviousVersionNumber = 0,
@@ -745,7 +741,6 @@ public async Task GivenNonUniqueTags_WhenInsertingTagDocuments_ThenReturnTrue()
{
new TransactionCommand
{
- PreviousSnapshot = default,
NextSnapshot = default!,
EntityId = Guid.NewGuid(),
ExpectedPreviousVersionNumber = 0,
@@ -759,7 +754,6 @@ public async Task GivenNonUniqueTags_WhenInsertingTagDocuments_ThenReturnTrue()
},
new TransactionCommand
{
- PreviousSnapshot = default,
NextSnapshot = default!,
EntityId = Guid.NewGuid(),
ExpectedPreviousVersionNumber = 0,
@@ -801,7 +795,6 @@ public async Task GivenNonUniqueLeases_WhenInsertingLeaseDocuments_ThenReturnFal
{
new TransactionCommand
{
- PreviousSnapshot = default,
NextSnapshot = default!,
EntityId = Guid.NewGuid(),
ExpectedPreviousVersionNumber = 0,
@@ -815,7 +808,6 @@ public async Task GivenNonUniqueLeases_WhenInsertingLeaseDocuments_ThenReturnFal
},
new TransactionCommand
{
- PreviousSnapshot = default,
NextSnapshot = default!,
EntityId = Guid.NewGuid(),
ExpectedPreviousVersionNumber = 0,
@@ -852,7 +844,7 @@ public async Task GivenEntityInserted_WhenGettingEntity_ThenReturnEntity()
await using var transactionRepository = await CreateRepository();
- var entityRepository = new EntityRepository(_serviceProvider, transactionRepository);
+ var entityRepository = EntityRepository.Create(_serviceProvider, transactionRepository);
var transaction = BuildTransaction(Guid.NewGuid(), entityId, new NoSource(),
new ICommand[] { new DoNothing() });
@@ -861,7 +853,7 @@ public async Task GivenEntityInserted_WhenGettingEntity_ThenReturnEntity()
// ACT
- var actualEntity = await entityRepository.Get(entityId);
+ var actualEntity = await entityRepository.GetCurrentOrConstruct(entityId);
// ASSERT
@@ -873,7 +865,7 @@ public async Task GivenEntityInsertedWithTags_WhenRemovingAllTags_ThenFinalEntit
{
// ARRANGE
- var transactionBuilder = _serviceProvider.GetTransactionBuilder();
+ var transactionBuilder = _serviceProvider.GetRequiredService>();
var expectedInitialTags = new[] { new Tag("Foo", "Bar") }.ToImmutableArray();
@@ -913,7 +905,7 @@ public async Task GivenEntityInsertedWithLeases_WhenRemovingAllLeases_ThenFinalE
{
// ARRANGE
- var transactionBuilder = _serviceProvider.GetTransactionBuilder();
+ var transactionBuilder = _serviceProvider.GetRequiredService>();
var expectedInitialLeases = new[] { new Lease("Foo", "Bar", "Baz") }.ToImmutableArray();
@@ -956,13 +948,13 @@ public async Task GivenTransactionCreatesEntity_WhenQueryingForVersionOne_ThenRe
var expectedCommand = new Count(1);
var transaction = _serviceProvider
- .GetTransactionBuilder()
+ .GetRequiredService>()
.Create(Guid.NewGuid(), expectedCommand)
.Build(Guid.NewGuid(), new NoSource());
var versionOneCommandQuery = new EntityVersionNumberQuery(1, 1);
- using var transactionRepository = await CreateRepository();
+ await using var transactionRepository = await CreateRepository();
// ACT
@@ -991,7 +983,7 @@ public async Task
var entityId = Guid.NewGuid();
- var transactionBuilder = _serviceProvider.GetTransactionBuilder();
+ var transactionBuilder = _serviceProvider.GetRequiredService>();
var firstTransaction = transactionBuilder
.Create(entityId, new Count(1))
@@ -1003,7 +995,7 @@ public async Task
var versionTwoCommandQuery = new EntityVersionNumberQuery(2, 2);
- using var transactionRepository = await CreateRepository();
+ await using var transactionRepository = await CreateRepository();
await transactionRepository.PutTransaction(firstTransaction);
@@ -1051,7 +1043,7 @@ public async Task GivenTransactionAlreadyInserted_WhenQueryingByTransactionTimeS
var commands = new ICommand[] { new Count(i) };
- var facts = new[] { new Counted(i), _serviceProvider.GetVersionNumberFact(1) };
+ var facts = new[] { new Counted(i), _serviceProvider.GetRequiredService>().GetVersionNumberFact(1) };
var leases = new[] { new CountLease(i) };
@@ -1117,7 +1109,7 @@ public async Task GivenTransactionAlreadyInserted_WhenQueryingByTransactionId_Th
var commands = new ICommand[] { new Count(i) };
- var facts = new[] { new Counted(i), _serviceProvider.GetVersionNumberFact(1) };
+ var facts = new[] { new Counted(i), _serviceProvider.GetRequiredService>().GetVersionNumberFact(1) };
var leases = new[] { new CountLease(i) };
@@ -1177,7 +1169,7 @@ public async Task GivenTransactionAlreadyInserted_WhenQueryingByEntityId_ThenRet
var commands = new ICommand[] { new Count(i) };
- var facts = new[] { new Counted(i), _serviceProvider.GetVersionNumberFact(1) };
+ var facts = new[] { new Counted(i), _serviceProvider.GetRequiredService>().GetVersionNumberFact(1) };
var leases = new[] { new CountLease(i) };
@@ -1229,7 +1221,7 @@ public async Task GivenTransactionAlreadyInserted_WhenQueryingByEntityVersionNum
var facts = new[]
{
- new Counted(i), _serviceProvider.GetVersionNumberFact((ulong)i)
+ new Counted(i), _serviceProvider.GetRequiredService>().GetVersionNumberFact((ulong)i)
};
var leases = new[] { new CountLease(i) };
diff --git a/src/EntityDb.Common/Entities/EntityRepository.cs b/src/EntityDb.Common/Entities/EntityRepository.cs
index e6699dda..8df4f4b0 100644
--- a/src/EntityDb.Common/Entities/EntityRepository.cs
+++ b/src/EntityDb.Common/Entities/EntityRepository.cs
@@ -1,75 +1,99 @@
using EntityDb.Abstractions.Entities;
+using EntityDb.Abstractions.Loggers;
using EntityDb.Abstractions.Snapshots;
+using EntityDb.Abstractions.Strategies;
using EntityDb.Abstractions.Transactions;
using EntityDb.Common.Extensions;
using EntityDb.Common.Queries;
+using Microsoft.Extensions.DependencyInjection;
using System;
+using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
-using System.Linq;
using System.Threading.Tasks;
namespace EntityDb.Common.Entities
{
internal class EntityRepository : IEntityRepository
{
- private readonly IServiceProvider _serviceProvider;
+ private readonly IConstructingStrategy _constructingStrategy;
+ private readonly IVersioningStrategy _versioningStrategy;
+ private readonly ILogger _logger;
+ private readonly IEnumerable> _transactionSubscribers;
+ private readonly ITransactionRepository _transactionRepository;
+ private readonly ISnapshotRepository? _snapshotRepository;
public EntityRepository
(
- IServiceProvider serviceProvider,
+ ILoggerFactory loggerFactory,
+ IConstructingStrategy constructingStrategy,
+ IVersioningStrategy versioningStrategy,
+ IEnumerable> transactionSubscribers,
ITransactionRepository transactionRepository,
ISnapshotRepository? snapshotRepository = null
)
{
- _serviceProvider = serviceProvider;
- TransactionRepository = transactionRepository;
- SnapshotRepository = snapshotRepository;
+ _logger = loggerFactory.CreateLogger>();
+ _constructingStrategy = constructingStrategy;
+ _versioningStrategy = versioningStrategy;
+ _transactionSubscribers = transactionSubscribers;
+ _transactionRepository = transactionRepository;
+ _snapshotRepository = snapshotRepository;
}
-
- public ITransactionRepository TransactionRepository { get; }
-
- public ISnapshotRepository? SnapshotRepository { get; }
-
- public async Task Get(Guid entityId)
+
+ private void Publish(ITransaction transaction)
{
- TEntity? snapshot = default;
-
- if (SnapshotRepository != null)
+ foreach (var transactionSubscriber in _transactionSubscribers)
{
- snapshot = await SnapshotRepository.GetSnapshot(entityId);
+ try
+ {
+ transactionSubscriber.Notify(transaction);
+ }
+ catch (Exception exception)
+ {
+ _logger.LogError(exception, $"{transactionSubscriber.GetType()}.{nameof(transactionSubscriber.Notify)}({transaction.Id})");
+ }
}
+ }
+
+ public async Task GetSnapshotOrDefault(Guid entityId)
+ {
+ if (_snapshotRepository != null)
+ {
+ return await _snapshotRepository.GetSnapshot(entityId);
+ }
+
+ return default;
+ }
- var entity = snapshot ?? _serviceProvider.Construct(entityId);
+ public async Task GetCurrentOrConstruct(Guid entityId)
+ {
+ var snapshot = await GetSnapshotOrDefault(entityId);
- var versionNumber = _serviceProvider.GetVersionNumber(entity);
+ var entity = snapshot ?? _constructingStrategy.Construct(entityId);
+
+ var versionNumber = _versioningStrategy.GetVersionNumber(entity);
var factQuery = new GetEntityQuery(entityId, versionNumber);
- var facts = await TransactionRepository.GetFacts(factQuery);
+ var facts = await _transactionRepository.GetFacts(factQuery);
entity = entity.Reduce(facts);
return entity;
}
- public Task Put(ITransaction transaction)
+ public async Task PutTransaction(ITransaction transaction)
{
- if (SnapshotRepository != null)
- {
- var lastCommands = transaction.Commands
- .GroupBy(command => command.EntityId)
- .Select(group => group.Last());
+ var success = await _transactionRepository.PutTransaction(transaction);
- foreach (var lastCommand in lastCommands)
- {
- if (_serviceProvider.ShouldPutSnapshot(lastCommand.PreviousSnapshot, lastCommand.NextSnapshot))
- {
- SnapshotRepository.PutSnapshot(lastCommand.EntityId, lastCommand.NextSnapshot);
- }
- }
+ if (success == false)
+ {
+ return false;
}
- return TransactionRepository.PutTransaction(transaction);
+ Publish(transaction);
+
+ return true;
}
[ExcludeFromCodeCoverage]
@@ -80,12 +104,28 @@ public void Dispose()
public async ValueTask DisposeAsync()
{
- await TransactionRepository.DisposeAsync();
+ await _transactionRepository.DisposeAsync();
- if (SnapshotRepository != null)
+ if (_snapshotRepository != null)
+ {
+ await _snapshotRepository.DisposeAsync();
+ }
+ }
+
+ public static EntityRepository Create
+ (
+ IServiceProvider serviceProvider,
+ ITransactionRepository transactionRepository,
+ ISnapshotRepository? snapshotRepository = null
+ )
+ {
+ if (snapshotRepository == null)
{
- await SnapshotRepository.DisposeAsync();
+ return ActivatorUtilities.CreateInstance>(serviceProvider, transactionRepository);
}
+
+ return ActivatorUtilities.CreateInstance>(serviceProvider, transactionRepository,
+ snapshotRepository);
}
}
}
diff --git a/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs
new file mode 100644
index 00000000..a12064b7
--- /dev/null
+++ b/src/EntityDb.Common/Entities/EntityRepositoryFactory.cs
@@ -0,0 +1,40 @@
+using EntityDb.Abstractions.Entities;
+using EntityDb.Abstractions.Snapshots;
+using EntityDb.Abstractions.Transactions;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Threading.Tasks;
+
+namespace EntityDb.Common.Entities
+{
+ internal class EntityRepositoryFactory : IEntityRepositoryFactory
+ {
+ private readonly IServiceProvider _serviceProvider;
+ private readonly ITransactionRepositoryFactory _transactionRepositoryFactory;
+ private readonly ISnapshotRepositoryFactory? _snapshotRepositoryFactory;
+
+ public EntityRepositoryFactory(IServiceProvider serviceProvider, ITransactionRepositoryFactory transactionRepositoryFactory,
+ ISnapshotRepositoryFactory? snapshotRepositoryFactory = null)
+ {
+ _serviceProvider = serviceProvider;
+ _transactionRepositoryFactory = transactionRepositoryFactory;
+ _snapshotRepositoryFactory = snapshotRepositoryFactory;
+ }
+
+ public async Task> CreateRepository(ITransactionSessionOptions transactionSessionOptions,
+ ISnapshotSessionOptions? snapshotSessionOptions = null)
+ {
+ var transactionRepository = await _transactionRepositoryFactory.CreateRepository(transactionSessionOptions);
+
+ if (_snapshotRepositoryFactory == null || snapshotSessionOptions == null)
+ {
+ return ActivatorUtilities.CreateInstance>(_serviceProvider,
+ transactionRepository);
+ }
+
+ var snapshotRepository = await _snapshotRepositoryFactory.CreateRepository(snapshotSessionOptions);
+
+ return ActivatorUtilities.CreateInstance>(_serviceProvider, transactionRepository, snapshotRepository);
+ }
+ }
+}
diff --git a/src/EntityDb.Common/Entities/IAuthorizedEntity.cs b/src/EntityDb.Common/Entities/IAuthorizedEntity.cs
index ae774235..068e8541 100644
--- a/src/EntityDb.Common/Entities/IAuthorizedEntity.cs
+++ b/src/EntityDb.Common/Entities/IAuthorizedEntity.cs
@@ -10,7 +10,7 @@ namespace EntityDb.Common.Entities
/// The type of the entity to be authorized.
public interface IAuthorizedEntity
{
- ///
+ ///
bool IsAuthorized(ICommand command, IAgent agent);
}
}
diff --git a/src/EntityDb.Common/Extensions/IQueryExtensions.cs b/src/EntityDb.Common/Extensions/IQueryExtensions.cs
index 6157c171..6ca4e003 100644
--- a/src/EntityDb.Common/Extensions/IQueryExtensions.cs
+++ b/src/EntityDb.Common/Extensions/IQueryExtensions.cs
@@ -1,6 +1,6 @@
using EntityDb.Abstractions.Queries;
+using EntityDb.Abstractions.Queries.Filters;
using EntityDb.Common.Queries.Filtered;
-using EntityDb.Common.Queries.Filters;
using EntityDb.Common.Queries.Modified;
namespace EntityDb.Common.Extensions
diff --git a/src/EntityDb.Common/Extensions/IServiceCollectionExtensions.cs b/src/EntityDb.Common/Extensions/IServiceCollectionExtensions.cs
index a371cffe..c53dc019 100644
--- a/src/EntityDb.Common/Extensions/IServiceCollectionExtensions.cs
+++ b/src/EntityDb.Common/Extensions/IServiceCollectionExtensions.cs
@@ -1,13 +1,16 @@
using EntityDb.Abstractions.Agents;
+using EntityDb.Abstractions.Entities;
using EntityDb.Abstractions.Loggers;
using EntityDb.Abstractions.Strategies;
using EntityDb.Common.Entities;
using EntityDb.Common.Loggers;
using EntityDb.Common.Strategies;
using EntityDb.Common.Strategies.Resolving;
+using EntityDb.Common.Transactions;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Reflection;
+using System.Runtime.CompilerServices;
namespace EntityDb.Common.Extensions
{
@@ -76,44 +79,6 @@ public static void AddAgentAccessor(this IServiceCollection serv
serviceCollection.AddScoped();
}
- ///
- /// Adds a custom implementation of to a service collection.
- ///
- /// The type of the entity to be snapshotted.
- /// The type that implements .
- /// The service collection
- public static void AddSnapshottingStrategy(
- this IServiceCollection serviceCollection)
- where TSnapshottingStrategy : class, ISnapshottingStrategy
- {
- serviceCollection.AddSingleton, TSnapshottingStrategy>();
- }
-
- ///
- /// Adds a custom implementation of to a service collection.
- ///
- /// The type of the entity to be constructed.
- /// The type that implements .
- /// The service collection
- public static void AddConstructingStrategy(
- this IServiceCollection serviceCollection)
- where TConstructingStrategy : class, IConstructingStrategy
- {
- serviceCollection.AddSingleton, TConstructingStrategy>();
- }
-
- ///
- /// Adds an internal implementation of to a service collection for an
- /// entity that implements .
- ///
- /// The type of the entity to be versioned.
- /// The service collection.
- public static void AddVersionedEntityVersioningStrategy(this IServiceCollection serviceCollection)
- where TEntity : IVersionedEntity
- {
- serviceCollection.AddSingleton, VersionedEntityVersioningStrategy>();
- }
-
///
/// Adds an internal implementation of to a service collection for an entity
/// that implements .
@@ -150,5 +115,18 @@ public static void AddAuthorizedEntityAuthorizingStrategy(this IService
serviceCollection
.AddSingleton, AuthorizedEntityAuthorizingStrategy>();
}
+
+ public static void AddEntity(this IServiceCollection serviceCollection)
+ where TEntity : IVersionedEntity
+ where TConstructingStrategy : class, IConstructingStrategy
+ {
+ serviceCollection.AddTransient>();
+
+ serviceCollection.AddTransient, EntityRepositoryFactory>();
+
+ serviceCollection.AddSingleton, TConstructingStrategy>();
+
+ serviceCollection.AddSingleton, VersionedEntityVersioningStrategy>();
+ }
}
}
diff --git a/src/EntityDb.Common/Extensions/IServiceProviderExtensions.cs b/src/EntityDb.Common/Extensions/IServiceProviderExtensions.cs
deleted file mode 100644
index 9fe6855d..00000000
--- a/src/EntityDb.Common/Extensions/IServiceProviderExtensions.cs
+++ /dev/null
@@ -1,249 +0,0 @@
-using EntityDb.Abstractions.Agents;
-using EntityDb.Abstractions.Commands;
-using EntityDb.Abstractions.Entities;
-using EntityDb.Abstractions.Facts;
-using EntityDb.Abstractions.Leases;
-using EntityDb.Abstractions.Snapshots;
-using EntityDb.Abstractions.Strategies;
-using EntityDb.Abstractions.Tags;
-using EntityDb.Abstractions.Transactions;
-using EntityDb.Common.Entities;
-using EntityDb.Common.Transactions;
-using Microsoft.Extensions.DependencyInjection;
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-
-namespace EntityDb.Common.Extensions
-{
- ///
- /// Extensions for service providers.
- ///
- public static class IServiceProviderExtensions
- {
- ///
- /// Returns the associated with the current service scope.
- ///
- /// The service provider.
- ///
- public static IAgent GetAgent(this IServiceProvider serviceProvider)
- {
- var agentAccessor = serviceProvider.GetRequiredService();
-
- return agentAccessor.GetAgent();
- }
-
- ///
- /// Returns a new instance of .
- ///
- /// The type of the entity for which a transaction is to be built.
- /// The service provider.
- ///
- public static TransactionBuilder GetTransactionBuilder(this IServiceProvider serviceProvider)
- {
- return new TransactionBuilder(serviceProvider);
- }
-
- ///
- /// Returns the resolved or throws if the cannot be resolved.
- ///
- /// The servie provider.
- /// Describes the type that needs to be resolved.
- /// The resolved .
- public static Type ResolveType(this IServiceProvider serviceProvider,
- IReadOnlyDictionary headers)
- {
- var resolvingStrategyChain = serviceProvider.GetRequiredService();
-
- return resolvingStrategyChain.ResolveType(headers);
- }
-
- ///
- /// Creates a new instance of .
- ///
- /// The type of the entity.
- /// The service provider.
- /// The agent's use case for the repository.
- /// A new instance of .
- public static Task> CreateTransactionRepository(
- this IServiceProvider serviceProvider, ITransactionSessionOptions transactionSessionOptions)
- {
- var transactionRepositoryFactory =
- serviceProvider.GetRequiredService>();
-
- return transactionRepositoryFactory.CreateRepository(transactionSessionOptions);
- }
-
- ///
- /// Create a new instance of
- ///
- /// The type of the entity.
- /// The service provider.
- /// The agent's use case for the repository.
- /// A new instance of .
- public static Task> CreateSnapshotRepository(
- this IServiceProvider serviceProvider, ISnapshotSessionOptions snapshotSessionOptions)
- {
- var snapshotRepositoryFactory = serviceProvider.GetRequiredService>();
-
- return snapshotRepositoryFactory.CreateRepository(snapshotSessionOptions);
- }
-
- ///
- /// Create a new instance of
- ///
- /// The type of the entity.
- /// The service provider.
- /// The agent's use case for the nested transaction repository.
- /// The agent's use case for the nested snapshot repository, if needed.
- ///
- public static async Task> CreateEntityRepository(
- this IServiceProvider serviceProvider, ITransactionSessionOptions transactionSessionOptions,
- ISnapshotSessionOptions? snapshotSessionOptions = null)
- {
- var transactionRepositoryFactory =
- serviceProvider.GetRequiredService>();
-
- var transactionRepository = await transactionRepositoryFactory.CreateRepository(transactionSessionOptions);
-
- if (snapshotSessionOptions == null)
- {
- return new EntityRepository(serviceProvider, transactionRepository);
- }
-
- var snapshotRepositoryFactory = serviceProvider.GetRequiredService>();
-
- var snapshotRepository = await snapshotRepositoryFactory.CreateRepository(snapshotSessionOptions);
-
- return new EntityRepository(serviceProvider, transactionRepository, snapshotRepository);
- }
-
- ///
- /// Returns a new instance of a .
- ///
- /// The type of the entity.
- /// The service provider.
- /// The id of the entity.
- /// A new instance of .
- public static TEntity Construct(this IServiceProvider serviceProvider, Guid entityId)
- {
- var constructingStrategy = serviceProvider.GetRequiredService>();
-
- return constructingStrategy.Construct(entityId);
- }
-
- ///
- /// Returns the version number of a .
- ///
- /// The type of the entity.
- /// The service provider.
- /// The entity.
- /// The version number of .
- public static ulong GetVersionNumber(this IServiceProvider serviceProvider, TEntity entity)
- {
- var versioningStrategy = serviceProvider.GetRequiredService>();
-
- return versioningStrategy.GetVersionNumber(entity);
- }
-
- ///
- /// Creates a new version number modifier for an entity.
- ///
- /// The type of the entity.
- /// The service provider.
- /// The desired version number.
- /// A new version number modifier for on an entity.
- public static IFact GetVersionNumberFact(this IServiceProvider serviceProvider,
- ulong versionNumber)
- {
- var versioningStrategy = serviceProvider.GetRequiredService>();
-
- return versioningStrategy.GetVersionNumberFact(versionNumber);
- }
-
- ///
- /// Returns the leases for a .
- ///
- /// The type of the entity.
- /// The service provider.
- /// The entity.
- /// The leases for .
- public static ILease[] GetLeases(this IServiceProvider serviceProvider, TEntity entity)
- {
- var leasingStrategy = serviceProvider.GetService>();
-
- if (leasingStrategy != null)
- {
- return leasingStrategy.GetLeases(entity);
- }
-
- return Array.Empty();
- }
-
- ///
- /// Returns the tags for a .
- ///
- /// The type of the entity.
- /// The service provider.
- /// The entity.
- /// The tags for .
- public static ITag[] GetTags(this IServiceProvider serviceProvider, TEntity entity)
- {
- var taggingStrategy = serviceProvider.GetService>();
-
- if (taggingStrategy != null)
- {
- return taggingStrategy.GetTags(entity);
- }
-
- return Array.Empty();
- }
-
- ///
- /// Determines if the agent is authorized to execute a command on an entity.
- ///
- /// The type of the entity.
- /// The service provider.
- /// The entity.
- /// The command.
- /// true if execution is authorized, or false if execution is not authorized.
- public static bool IsAuthorized(this IServiceProvider serviceProvider, TEntity entity,
- ICommand command)
- {
- var authorizingStrategy = serviceProvider.GetService>();
-
- if (authorizingStrategy != null)
- {
- var agent = serviceProvider.GetAgent();
-
- return authorizingStrategy.IsAuthorized(entity, command, agent);
- }
-
- return true;
- }
-
- ///