diff --git a/Backend.Fx.sln b/Backend.Fx.sln
index 471ddf96..c2035662 100644
--- a/Backend.Fx.sln
+++ b/Backend.Fx.sln
@@ -45,7 +45,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Backend.Fx.InMemoryPersiste
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Backend.Fx.Log4NetLogging", "src\implementations\Backend.Fx.Log4NetLogging\Backend.Fx.Log4NetLogging.csproj", "{C27BA4CE-882B-405F-906E-4DFA6E9F1216}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{DFD5E4B8-2479-4D29-9857-9199B94E412A}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Backend.Fx.RabbitMq.Tests", "tests\Backend.Fx.RabbitMq.Tests\Backend.Fx.RabbitMq.Tests.csproj", "{6D0A5E9D-2FA5-4CC9-96B0-C2C871335E3A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Backend.Fx.SerilogLogging", "src\implementations\Backend.Fx.SerilogLogging\Backend.Fx.SerilogLogging.csproj", "{33F7D896-2276-4DD7-A4DA-8FD5C47F7735}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -108,6 +110,14 @@ Global
{C27BA4CE-882B-405F-906E-4DFA6E9F1216}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C27BA4CE-882B-405F-906E-4DFA6E9F1216}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C27BA4CE-882B-405F-906E-4DFA6E9F1216}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6D0A5E9D-2FA5-4CC9-96B0-C2C871335E3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6D0A5E9D-2FA5-4CC9-96B0-C2C871335E3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6D0A5E9D-2FA5-4CC9-96B0-C2C871335E3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6D0A5E9D-2FA5-4CC9-96B0-C2C871335E3A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {33F7D896-2276-4DD7-A4DA-8FD5C47F7735}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {33F7D896-2276-4DD7-A4DA-8FD5C47F7735}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {33F7D896-2276-4DD7-A4DA-8FD5C47F7735}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {33F7D896-2276-4DD7-A4DA-8FD5C47F7735}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -129,6 +139,8 @@ Global
{45EC5987-1C85-4940-8E5E-3B4F0FA90AF8} = {56ACAE69-F7F0-4FF2-BEE6-4B079481CF9A}
{0B8F13CA-1347-4655-9D41-AED21B1AFAC4} = {739A7296-579F-4D9A-BC73-DCECD260D7A0}
{C27BA4CE-882B-405F-906E-4DFA6E9F1216} = {739A7296-579F-4D9A-BC73-DCECD260D7A0}
+ {6D0A5E9D-2FA5-4CC9-96B0-C2C871335E3A} = {C7885592-A4B8-4BA8-8D3A-1EDA4025D17A}
+ {33F7D896-2276-4DD7-A4DA-8FD5C47F7735} = {739A7296-579F-4D9A-BC73-DCECD260D7A0}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {45648557-C751-44AD-9C87-0F12EB673969}
diff --git a/README.md b/README.md
index 9b0a04ae..6deeb34f 100644
--- a/README.md
+++ b/README.md
@@ -24,5 +24,5 @@ Vendor | Library | .NET Standard | NuGet
[Entity Framework Core 2.1](https://github.com/aspnet/EntityFramework) as persistence mechanism | Backend.Fx.EfCorePersistence | 2.0 | [![NuGet](https://img.shields.io/nuget/v/Backend.Fx.EfCorePersistence.svg)](https://www.nuget.org/packages/Backend.Fx.EfCorePersistence)
InMemory Persistence implementation | Backend.Fx.InMemoryPersistence | 1.3 | [![NuGet](https://img.shields.io/nuget/v/Backend.Fx.InMemoryPersistence.svg)](https://www.nuget.org/packages/Backend.Fx.InMemoryPersistence)
[NLog](https://github.com/NLog/NLog) logging | Backend.Fx.NLogLogging | 1.6 | [![NuGet](https://img.shields.io/nuget/v/Backend.Fx.NLogLogging.svg)](https://www.nuget.org/packages/Backend.Fx.NLogLogging)
-[RabbitMq](https://www.rabbitmq.com/) Event Bus | Backend.Fx.RabbitMq | 1.5 | [![NuGet](https://img.shields.io/nuget/v/Backend.Fx.RabbitMq.svg)](https://www.nuget.org/packages/Backend.Fx.RabbitMq)
+[RabbitMq](https://www.rabbitmq.com/) Message Bus | Backend.Fx.RabbitMq | 1.5 | [![NuGet](https://img.shields.io/nuget/v/Backend.Fx.RabbitMq.svg)](https://www.nuget.org/packages/Backend.Fx.RabbitMq)
[Simple Injector](https://github.com/simpleinjector/SimpleInjector) as DI container | Backend.Fx.SimpleInjectorDependencyInjection | 1.3 | [![NuGet](https://img.shields.io/nuget/v/Backend.Fx.SimpleInjectorDependencyInjection.svg)](https://www.nuget.org/packages/Backend.Fx.SimpleInjectorDependencyInjection)
diff --git a/src/abstractions/Backend.Fx/Backend.Fx.csproj b/src/abstractions/Backend.Fx/Backend.Fx.csproj
index 5d9d5a45..3f24de27 100644
--- a/src/abstractions/Backend.Fx/Backend.Fx.csproj
+++ b/src/abstractions/Backend.Fx/Backend.Fx.csproj
@@ -30,14 +30,14 @@
-
+
-
+
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/BuildingBlocks/AggregateRoot.cs b/src/abstractions/Backend.Fx/BuildingBlocks/AggregateRoot.cs
index e06e7fdd..e22101f1 100644
--- a/src/abstractions/Backend.Fx/BuildingBlocks/AggregateRoot.cs
+++ b/src/abstractions/Backend.Fx/BuildingBlocks/AggregateRoot.cs
@@ -7,10 +7,13 @@
public abstract class AggregateRoot : Entity
{
protected AggregateRoot()
- { }
+ {
+ }
- protected AggregateRoot(int id) : base(id) { }
+ protected AggregateRoot(int id) : base(id)
+ {
+ }
public int TenantId { get; set; }
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/BuildingBlocks/Entity.cs b/src/abstractions/Backend.Fx/BuildingBlocks/Entity.cs
index 9b9175a6..5ddc4d22 100644
--- a/src/abstractions/Backend.Fx/BuildingBlocks/Entity.cs
+++ b/src/abstractions/Backend.Fx/BuildingBlocks/Entity.cs
@@ -1,39 +1,32 @@
-using Backend.Fx.Extensions;
+using System;
+using System.ComponentModel.DataAnnotations;
+using Backend.Fx.Extensions;
+using JetBrains.Annotations;
namespace Backend.Fx.BuildingBlocks
{
- using System;
- using System.ComponentModel.DataAnnotations;
- using System.Diagnostics;
- using JetBrains.Annotations;
-
///
/// An object that is not defined by its attributes, but rather by a thread of continuity and its identity.
/// https://en.wikipedia.org/wiki/Domain-driven_design#Building_blocks
///
- [DebuggerDisplay("{" + nameof(DebuggerDisplay) + "}")]
public abstract class Entity : Identified
{
protected Entity()
- { }
+ {
+ }
protected Entity(int id)
{
Id = id;
}
- [UsedImplicitly]
- public string DebuggerDisplay => $"{GetType().Name}[{Id}]";
-
public DateTime CreatedOn { get; protected set; }
- [StringLength(100)]
- public string CreatedBy { get; protected set; }
+ [StringLength(100)] public string CreatedBy { get; protected set; }
public DateTime? ChangedOn { get; protected set; }
- [StringLength(100)]
- public string ChangedBy { get; protected set; }
+ [StringLength(100)] public string ChangedBy { get; protected set; }
public void SetCreatedProperties([NotNull] string createdBy, DateTime createdOn)
{
@@ -41,10 +34,12 @@ public void SetCreatedProperties([NotNull] string createdBy, DateTime createdOn)
{
throw new ArgumentNullException(nameof(createdBy));
}
+
if (createdBy == string.Empty)
{
throw new ArgumentException(nameof(createdBy));
}
+
CreatedBy = createdBy.Cut(100);
CreatedOn = createdOn;
}
@@ -55,12 +50,14 @@ public void SetModifiedProperties([NotNull] string changedBy, DateTime changedOn
{
throw new ArgumentNullException(nameof(changedBy));
}
+
if (changedBy == string.Empty)
{
throw new ArgumentException(nameof(changedBy));
}
+
ChangedBy = changedBy.Cut(100);
ChangedOn = changedOn;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/BuildingBlocks/IApplicationService.cs b/src/abstractions/Backend.Fx/BuildingBlocks/IApplicationService.cs
index d0169f7f..832d8777 100644
--- a/src/abstractions/Backend.Fx/BuildingBlocks/IApplicationService.cs
+++ b/src/abstractions/Backend.Fx/BuildingBlocks/IApplicationService.cs
@@ -4,5 +4,6 @@
/// A marker interface to identify application services to be auto registered in the container on boot
///
public interface IApplicationService
- { }
-}
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/BuildingBlocks/IDomainService.cs b/src/abstractions/Backend.Fx/BuildingBlocks/IDomainService.cs
index 9a054a57..ddcc120d 100644
--- a/src/abstractions/Backend.Fx/BuildingBlocks/IDomainService.cs
+++ b/src/abstractions/Backend.Fx/BuildingBlocks/IDomainService.cs
@@ -4,5 +4,6 @@
/// A marker interface to domain application services to be auto registered in the container on boot
///
public interface IDomainService
- { }
-}
+ {
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/BuildingBlocks/IFullTextSearchService.cs b/src/abstractions/Backend.Fx/BuildingBlocks/IFullTextSearchService.cs
deleted file mode 100644
index 7fb552f3..00000000
--- a/src/abstractions/Backend.Fx/BuildingBlocks/IFullTextSearchService.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-
-namespace Backend.Fx.BuildingBlocks
-{
- using System.Linq;
-
- public interface IFullTextSearchService where TAggregateRoot : AggregateRoot
- {
- IQueryable Search(string searchQuery);
- }
-}
diff --git a/src/abstractions/Backend.Fx/BuildingBlocks/IRepository.cs b/src/abstractions/Backend.Fx/BuildingBlocks/IRepository.cs
index 2ed24ff7..19ba3187 100644
--- a/src/abstractions/Backend.Fx/BuildingBlocks/IRepository.cs
+++ b/src/abstractions/Backend.Fx/BuildingBlocks/IRepository.cs
@@ -1,8 +1,8 @@
-namespace Backend.Fx.BuildingBlocks
-{
- using System.Collections.Generic;
- using System.Linq;
+using System.Collections.Generic;
+using System.Linq;
+namespace Backend.Fx.BuildingBlocks
+{
///
/// Encapsulates methods for retrieving domain objects
/// See https://en.wikipedia.org/wiki/Domain-driven_design#Building_blocks
@@ -19,6 +19,5 @@ public interface IRepository where TAggregateRoot : AggregateRoo
bool Any();
TAggregateRoot[] Resolve(IEnumerable ids);
IQueryable AggregateQueryable { get; }
-
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/BuildingBlocks/IView.cs b/src/abstractions/Backend.Fx/BuildingBlocks/IView.cs
index 1772c19c..0121addb 100644
--- a/src/abstractions/Backend.Fx/BuildingBlocks/IView.cs
+++ b/src/abstractions/Backend.Fx/BuildingBlocks/IView.cs
@@ -1,13 +1,14 @@
-namespace Backend.Fx.BuildingBlocks
-{
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+namespace Backend.Fx.BuildingBlocks
+{
public interface IView : IQueryable
- {}
+ {
+ }
public abstract class View : IView
{
@@ -34,4 +35,4 @@ IEnumerator IEnumerable.GetEnumerator()
public IQueryProvider Provider => _viewImplementation.Provider;
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/BuildingBlocks/Identified.cs b/src/abstractions/Backend.Fx/BuildingBlocks/Identified.cs
index 1c1aa61c..f37a3bde 100644
--- a/src/abstractions/Backend.Fx/BuildingBlocks/Identified.cs
+++ b/src/abstractions/Backend.Fx/BuildingBlocks/Identified.cs
@@ -1,13 +1,16 @@
using System;
using System.ComponentModel.DataAnnotations;
+using System.Diagnostics;
+using JetBrains.Annotations;
namespace Backend.Fx.BuildingBlocks
{
+ [DebuggerDisplay("{" + nameof(DebuggerDisplay) + "}")]
public abstract class Identified : IEquatable
{
- [Key]
- public int Id { get; set; }
+ [Key] public int Id { get; set; }
+ [UsedImplicitly] public string DebuggerDisplay => $"{GetType().Name}[{Id}]";
public bool Equals(Identified other)
{
@@ -21,7 +24,7 @@ public bool Equals(Identified other)
public override bool Equals(object obj)
{
- Identified other = obj as Identified;
+ var other = obj as Identified;
if (other == null)
{
return false;
@@ -40,7 +43,7 @@ public override int GetHashCode()
// ReSharper enable NonReadonlyMemberInGetHashCode
// ReSharper disable once BaseObjectGetHashCodeCallInGetHashCode
- return base.GetHashCode();
+ return base.GetHashCode();
}
public static bool operator ==(Identified x, Identified y)
@@ -53,4 +56,4 @@ public override int GetHashCode()
return !(x == y);
}
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/BuildingBlocks/Repository.cs b/src/abstractions/Backend.Fx/BuildingBlocks/Repository.cs
index 591640d9..82783e43 100644
--- a/src/abstractions/Backend.Fx/BuildingBlocks/Repository.cs
+++ b/src/abstractions/Backend.Fx/BuildingBlocks/Repository.cs
@@ -1,16 +1,16 @@
-namespace Backend.Fx.BuildingBlocks
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Backend.Fx.Environment.MultiTenancy;
+using Backend.Fx.Exceptions;
+using Backend.Fx.Extensions;
+using Backend.Fx.Logging;
+using Backend.Fx.Patterns.Authorization;
+using Backend.Fx.Patterns.DependencyInjection;
+using JetBrains.Annotations;
+
+namespace Backend.Fx.BuildingBlocks
{
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using Environment.MultiTenancy;
- using Exceptions;
- using Extensions;
- using JetBrains.Annotations;
- using Logging;
- using Patterns.Authorization;
- using Patterns.DependencyInjection;
-
public abstract class Repository : IRepository where TAggregateRoot : AggregateRoot
{
private static readonly ILogger Logger = LogManager.Create>();
@@ -45,7 +45,7 @@ public IQueryable AggregateQueryable
public TAggregateRoot Single(int id)
{
Logger.Debug($"Getting single {AggregateTypeName}[{id}]");
- var aggregateRoot = AggregateQueryable.FirstOrDefault(aggr => aggr.Id.Equals(id));
+ TAggregateRoot aggregateRoot = AggregateQueryable.FirstOrDefault(aggr => aggr.Id.Equals(id));
if (aggregateRoot == null)
{
throw new NotFoundException(id);
diff --git a/src/abstractions/Backend.Fx/BuildingBlocks/ValueObject.cs b/src/abstractions/Backend.Fx/BuildingBlocks/ValueObject.cs
index e3c1fc76..f4d5a5f3 100644
--- a/src/abstractions/Backend.Fx/BuildingBlocks/ValueObject.cs
+++ b/src/abstractions/Backend.Fx/BuildingBlocks/ValueObject.cs
@@ -1,9 +1,9 @@
-namespace Backend.Fx.BuildingBlocks
-{
- using System;
- using System.Collections.Generic;
- using System.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+namespace Backend.Fx.BuildingBlocks
+{
///
/// An object that contains attributes but has no conceptual identity.
/// https://en.wikipedia.org/wiki/Domain-driven_design#Building_blocks
@@ -21,7 +21,7 @@ public override bool Equals(object obj)
if (ReferenceEquals(this, obj)) return true;
if (ReferenceEquals(null, obj)) return false;
if (GetType() != obj.GetType()) return false;
- return GetEqualityComponents().SequenceEqual(((ValueObject)obj).GetEqualityComponents());
+ return GetEqualityComponents().SequenceEqual(((ValueObject) obj).GetEqualityComponents());
}
public override int GetHashCode()
@@ -29,7 +29,10 @@ public override int GetHashCode()
unchecked
{
var hash = 17;
- foreach (var obj in GetEqualityComponents()) hash = hash * 23 + (obj != null ? obj.GetHashCode() : 0);
+ foreach (object obj in GetEqualityComponents())
+ {
+ hash = hash * 23 + (obj != null ? obj.GetHashCode() : 0);
+ }
return hash;
}
@@ -40,24 +43,38 @@ public abstract class ComparableValueObject : ValueObject, IComparable
{
public int CompareTo(object obj)
{
- if (ReferenceEquals(this, obj)) return 0;
- if (ReferenceEquals(null, obj)) return 1;
+ if (ReferenceEquals(this, obj))
+ {
+ return 0;
+ }
+
+ if (ReferenceEquals(null, obj))
+ {
+ return 1;
+ }
if (GetType() != obj.GetType())
+ {
throw new InvalidOperationException();
+ }
return CompareTo(obj as ComparableValueObject);
}
protected abstract IEnumerable GetComparableComponents();
- protected IComparable AsNonGenericComparable(IComparable comparable)
- {
- return new NonGenericComparable(comparable);
- }
-
protected int CompareTo(ComparableValueObject other)
{
+ if (ReferenceEquals(this, other))
+ {
+ return 0;
+ }
+
+ if (ReferenceEquals(null, other))
+ {
+ return 1;
+ }
+
using (var thisComponents = GetComparableComponents().GetEnumerator())
using (var otherComponents = other.GetComparableComponents().GetEnumerator())
{
@@ -66,12 +83,17 @@ protected int CompareTo(ComparableValueObject other)
var x = thisComponents.MoveNext();
var y = otherComponents.MoveNext();
if (x != y)
+ {
throw new InvalidOperationException();
+ }
+
if (x)
{
- var c = thisComponents.Current.CompareTo(otherComponents.Current);
+ var c = thisComponents.Current?.CompareTo(otherComponents.Current) ?? 0;
if (c != 0)
+ {
return c;
+ }
}
else
{
@@ -82,24 +104,6 @@ protected int CompareTo(ComparableValueObject other)
return 0;
}
}
-
- private class NonGenericComparable : IComparable
- {
- private readonly IComparable _comparable;
-
- public NonGenericComparable(IComparable comparable)
- {
- _comparable = comparable;
- }
-
- public int CompareTo(object obj)
- {
- if (ReferenceEquals(_comparable, obj)) return 0;
- if (ReferenceEquals(null, obj))
- throw new ArgumentNullException();
- return _comparable.CompareTo((T) obj);
- }
- }
}
public abstract class ComparableValueObject : ComparableValueObject, IComparable
diff --git a/src/abstractions/Backend.Fx/ConfigurationSettings/ISettingSerializer.cs b/src/abstractions/Backend.Fx/ConfigurationSettings/ISettingSerializer.cs
index d8738665..4fc9868d 100644
--- a/src/abstractions/Backend.Fx/ConfigurationSettings/ISettingSerializer.cs
+++ b/src/abstractions/Backend.Fx/ConfigurationSettings/ISettingSerializer.cs
@@ -1,11 +1,12 @@
+using System;
+using System.Globalization;
+using JetBrains.Annotations;
+
namespace Backend.Fx.ConfigurationSettings
{
- using System;
- using System.Globalization;
- using JetBrains.Annotations;
-
public interface ISettingSerializer
- { }
+ {
+ }
public interface ISettingSerializer : ISettingSerializer
{
@@ -37,7 +38,7 @@ public string Serialize(int? setting)
public int? Deserialize(string value)
{
- return string.IsNullOrWhiteSpace(value) ? (int?)null : int.Parse(value, CultureInfo.InvariantCulture);
+ return string.IsNullOrWhiteSpace(value) ? (int?) null : int.Parse(value, CultureInfo.InvariantCulture);
}
}
@@ -51,7 +52,7 @@ public string Serialize(double? setting)
public double? Deserialize(string value)
{
- return string.IsNullOrWhiteSpace(value) ? (double?)null : double.Parse(value, CultureInfo.InvariantCulture);
+ return string.IsNullOrWhiteSpace(value) ? (double?) null : double.Parse(value, CultureInfo.InvariantCulture);
}
}
@@ -65,7 +66,7 @@ public string Serialize(bool? setting)
public bool? Deserialize(string value)
{
- return string.IsNullOrWhiteSpace(value) ? (bool?)null : bool.Parse(value);
+ return string.IsNullOrWhiteSpace(value) ? (bool?) null : bool.Parse(value);
}
}
@@ -79,7 +80,7 @@ public string Serialize(DateTime? setting)
public DateTime? Deserialize(string value)
{
- return string.IsNullOrWhiteSpace(value) ? (DateTime?)null : DateTime.Parse(value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
+ return string.IsNullOrWhiteSpace(value) ? (DateTime?) null : DateTime.Parse(value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
}
}
}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/ConfigurationSettings/Setting.cs b/src/abstractions/Backend.Fx/ConfigurationSettings/Setting.cs
index 214d4605..c1e55a92 100644
--- a/src/abstractions/Backend.Fx/ConfigurationSettings/Setting.cs
+++ b/src/abstractions/Backend.Fx/ConfigurationSettings/Setting.cs
@@ -1,12 +1,14 @@
+using Backend.Fx.BuildingBlocks;
+using JetBrains.Annotations;
+
namespace Backend.Fx.ConfigurationSettings
{
- using BuildingBlocks;
- using JetBrains.Annotations;
-
public class Setting : AggregateRoot
{
[UsedImplicitly]
- private Setting() { }
+ private Setting()
+ {
+ }
public Setting(int id, string key) : base(id)
{
diff --git a/src/abstractions/Backend.Fx/ConfigurationSettings/SettingSerializerFactory.cs b/src/abstractions/Backend.Fx/ConfigurationSettings/SettingSerializerFactory.cs
index 6acec89e..a270989c 100644
--- a/src/abstractions/Backend.Fx/ConfigurationSettings/SettingSerializerFactory.cs
+++ b/src/abstractions/Backend.Fx/ConfigurationSettings/SettingSerializerFactory.cs
@@ -1,11 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using JetBrains.Annotations;
+
namespace Backend.Fx.ConfigurationSettings
{
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using JetBrains.Annotations;
-
public interface ISettingSerializerFactory
{
ISettingSerializer GetSerializer();
@@ -18,16 +18,16 @@ public class SettingSerializerFactory : ISettingSerializerFactory
public SettingSerializerFactory()
{
Serializers = typeof(ISettingSerializer)
- .GetTypeInfo()
- .Assembly
- .ExportedTypes
- .Select(t => t.GetTypeInfo())
- .Where(t => !t.IsAbstract && t.IsClass && typeof(ISettingSerializer).GetTypeInfo().IsAssignableFrom(t))
- .ToDictionary(
- t => t.ImplementedInterfaces
- .Single(i => i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == typeof(ISettingSerializer<>))
- .GenericTypeArguments.Single(),
- t => (ISettingSerializer) Activator.CreateInstance(t.AsType()));
+ .GetTypeInfo()
+ .Assembly
+ .ExportedTypes
+ .Select(t => t.GetTypeInfo())
+ .Where(t => !t.IsAbstract && t.IsClass && typeof(ISettingSerializer).GetTypeInfo().IsAssignableFrom(t))
+ .ToDictionary(
+ t => t.ImplementedInterfaces
+ .Single(i => i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == typeof(ISettingSerializer<>))
+ .GenericTypeArguments.Single(),
+ t => (ISettingSerializer) Activator.CreateInstance(t.AsType()));
}
[NotNull]
@@ -39,7 +39,7 @@ public ISettingSerializer GetSerializer()
}
throw new ArgumentOutOfRangeException(nameof(T),
- $"No Serializer for Setting Type {typeof(T).Name} available");
+ $"No Serializer for Setting Type {typeof(T).Name} available");
}
}
}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/ConfigurationSettings/SettingsService.cs b/src/abstractions/Backend.Fx/ConfigurationSettings/SettingsService.cs
index 59ad3bad..26476479 100644
--- a/src/abstractions/Backend.Fx/ConfigurationSettings/SettingsService.cs
+++ b/src/abstractions/Backend.Fx/ConfigurationSettings/SettingsService.cs
@@ -1,9 +1,9 @@
+using System.Linq;
+using Backend.Fx.BuildingBlocks;
+using Backend.Fx.Patterns.IdGeneration;
+
namespace Backend.Fx.ConfigurationSettings
{
- using System.Linq;
- using BuildingBlocks;
- using Patterns.IdGeneration;
-
public abstract class SettingsService
{
private readonly string _category;
@@ -27,6 +27,7 @@ protected T ReadSetting(string key)
{
return default(T);
}
+
var serializer = _settingSerializerFactory.GetSerializer();
return setting.GetValue(serializer);
}
@@ -40,6 +41,7 @@ protected void WriteSetting(string key, T value)
setting = new Setting(_idGenerator.NextId(), categoryKey);
_settingRepository.Add(setting);
}
+
var serializer = _settingSerializerFactory.GetSerializer();
setting.SetValue(serializer, value);
}
diff --git a/src/abstractions/Backend.Fx/Environment/Authentication/AnonymousIdentity.cs b/src/abstractions/Backend.Fx/Environment/Authentication/AnonymousIdentity.cs
index b564bbc0..3ef29a26 100644
--- a/src/abstractions/Backend.Fx/Environment/Authentication/AnonymousIdentity.cs
+++ b/src/abstractions/Backend.Fx/Environment/Authentication/AnonymousIdentity.cs
@@ -1,7 +1,7 @@
-namespace Backend.Fx.Environment.Authentication
-{
- using System.Security.Principal;
+using System.Security.Principal;
+namespace Backend.Fx.Environment.Authentication
+{
public class AnonymousIdentity : IIdentity
{
public string Name => "ANONYMOUS";
diff --git a/src/abstractions/Backend.Fx/Environment/Authentication/CurrentIdentityHolder.cs b/src/abstractions/Backend.Fx/Environment/Authentication/CurrentIdentityHolder.cs
index e93eea55..7c7cadb6 100644
--- a/src/abstractions/Backend.Fx/Environment/Authentication/CurrentIdentityHolder.cs
+++ b/src/abstractions/Backend.Fx/Environment/Authentication/CurrentIdentityHolder.cs
@@ -1,8 +1,8 @@
-namespace Backend.Fx.Environment.Authentication
-{
- using System.Security.Principal;
- using Patterns.DependencyInjection;
+using System.Security.Principal;
+using Backend.Fx.Patterns.DependencyInjection;
+namespace Backend.Fx.Environment.Authentication
+{
public class CurrentIdentityHolder : CurrentTHolder
{
public override IIdentity ProvideInstance()
@@ -28,4 +28,4 @@ public static ICurrentTHolder CreateSystem()
return currentIdentityHolder;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/Authentication/SystemIdentity.cs b/src/abstractions/Backend.Fx/Environment/Authentication/SystemIdentity.cs
index 525fc8e9..cb388295 100644
--- a/src/abstractions/Backend.Fx/Environment/Authentication/SystemIdentity.cs
+++ b/src/abstractions/Backend.Fx/Environment/Authentication/SystemIdentity.cs
@@ -1,7 +1,7 @@
-namespace Backend.Fx.Environment.Authentication
-{
- using System.Security.Principal;
+using System.Security.Principal;
+namespace Backend.Fx.Environment.Authentication
+{
public class SystemIdentity : IIdentity
{
public string Name => "SYSTEM";
@@ -10,4 +10,4 @@ public class SystemIdentity : IIdentity
public bool IsAuthenticated => true;
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/DateAndTime/AdjustableClock.cs b/src/abstractions/Backend.Fx/Environment/DateAndTime/AdjustableClock.cs
new file mode 100644
index 00000000..9af417ae
--- /dev/null
+++ b/src/abstractions/Backend.Fx/Environment/DateAndTime/AdjustableClock.cs
@@ -0,0 +1,34 @@
+using System;
+using Backend.Fx.Logging;
+
+namespace Backend.Fx.Environment.DateAndTime
+{
+ public class AdjustableClock : IClock
+ {
+ private static readonly ILogger Logger = LogManager.Create();
+
+ private readonly IClock _clockImplementation;
+ private DateTime? _overriddenUtcNow;
+
+ public AdjustableClock(IClock clockImplementation)
+ {
+ _clockImplementation = clockImplementation;
+ }
+
+ public DateTime UtcNow => _overriddenUtcNow ?? _clockImplementation.UtcNow;
+
+ public void OverrideUtcNow(DateTime utcNow)
+ {
+ Logger.Trace($"Adjusting clock to {utcNow}");
+ _overriddenUtcNow = utcNow;
+ }
+
+ public DateTime Advance(TimeSpan timespan)
+ {
+ _overriddenUtcNow = _overriddenUtcNow ?? _clockImplementation.UtcNow;
+ Logger.Trace($"Advancing clock by {timespan}");
+ _overriddenUtcNow = _overriddenUtcNow.Value.Add(timespan);
+ return _overriddenUtcNow.Value;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/DateAndTime/Clock.cs b/src/abstractions/Backend.Fx/Environment/DateAndTime/Clock.cs
deleted file mode 100644
index 2183439a..00000000
--- a/src/abstractions/Backend.Fx/Environment/DateAndTime/Clock.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Backend.Fx.Environment.DateAndTime
-{
- using System;
- using Logging;
-
- public abstract class Clock : IClock
- {
- private static readonly ILogger Logger = LogManager.Create();
-
- private DateTime? _utcNow;
-
- public DateTime UtcNow => _utcNow ?? DateTime.UtcNow;
-
- public void OverrideUtcNow(DateTime overriddenUtcNow)
- {
- Logger.Debug("Freezing clock at {0:O}", overriddenUtcNow);
- _utcNow = overriddenUtcNow;
- }
- }
-}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/DateAndTime/FrozenClock.cs b/src/abstractions/Backend.Fx/Environment/DateAndTime/FrozenClock.cs
index b3ff7a44..2bcf206f 100644
--- a/src/abstractions/Backend.Fx/Environment/DateAndTime/FrozenClock.cs
+++ b/src/abstractions/Backend.Fx/Environment/DateAndTime/FrozenClock.cs
@@ -1,23 +1,22 @@
-namespace Backend.Fx.Environment.DateAndTime
-{
- using System;
+using System;
+using Backend.Fx.Logging;
+namespace Backend.Fx.Environment.DateAndTime
+{
///
/// Best practice for web (service) applications: time does not advance during a single request
///
- public class FrozenClock : Clock
+ public class FrozenClock : IClock
{
- public FrozenClock() : this(DateTime.UtcNow)
- { }
-
- private FrozenClock(DateTime utcNow)
+ private static readonly ILogger Logger = LogManager.Create();
+
+ // ReSharper disable once UnusedParameter.Local
+ public FrozenClock(IClock clock)
{
- OverrideUtcNow(utcNow);
+ UtcNow = DateTime.UtcNow;
+ Logger.Trace($"Freezing clock at {UtcNow}");
}
- public static IClock WithFrozenUtcNow(DateTime utcNow)
- {
- return new FrozenClock(utcNow);
- }
+ public DateTime UtcNow { get; }
}
}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/DateAndTime/IClock.cs b/src/abstractions/Backend.Fx/Environment/DateAndTime/IClock.cs
index 51cd1822..dcd4a886 100644
--- a/src/abstractions/Backend.Fx/Environment/DateAndTime/IClock.cs
+++ b/src/abstractions/Backend.Fx/Environment/DateAndTime/IClock.cs
@@ -1,7 +1,7 @@
-namespace Backend.Fx.Environment.DateAndTime
-{
- using System;
+using System;
+namespace Backend.Fx.Environment.DateAndTime
+{
///
/// Wraps access to DateTime.UtcNow. By means of this interface the current time can be mocked.
/// the database should only store universal date and time values, that could be translated into user's time by applying a UtcOffset
@@ -9,7 +9,5 @@
public interface IClock
{
DateTime UtcNow { get; }
-
- void OverrideUtcNow(DateTime utcNow);
}
}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/DateAndTime/WallClock.cs b/src/abstractions/Backend.Fx/Environment/DateAndTime/WallClock.cs
index 3de01b89..5ca43a75 100644
--- a/src/abstractions/Backend.Fx/Environment/DateAndTime/WallClock.cs
+++ b/src/abstractions/Backend.Fx/Environment/DateAndTime/WallClock.cs
@@ -1,8 +1,12 @@
-namespace Backend.Fx.Environment.DateAndTime
+using System;
+
+namespace Backend.Fx.Environment.DateAndTime
{
///
/// The real system clock
///
- public class WallClock : Clock
- { }
-}
+ public class WallClock : IClock
+ {
+ public DateTime UtcNow => DateTime.UtcNow;
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/AllTenantBackendFxApplicationInvoker.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/AllTenantBackendFxApplicationInvoker.cs
new file mode 100644
index 00000000..4d89d2c2
--- /dev/null
+++ b/src/abstractions/Backend.Fx/Environment/MultiTenancy/AllTenantBackendFxApplicationInvoker.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Linq;
+using Backend.Fx.Environment.Authentication;
+using Backend.Fx.Logging;
+using Backend.Fx.Patterns.DependencyInjection;
+
+namespace Backend.Fx.Environment.MultiTenancy
+{
+ public class AllTenantBackendFxApplicationInvoker
+ {
+ private static readonly ILogger Logger = LogManager.Create();
+ private readonly ITenantIdProvider _tenantIdProvider;
+ private readonly IBackendFxApplicationInvoker _invoker;
+
+ public AllTenantBackendFxApplicationInvoker(ITenantIdProvider tenantIdProvider, IBackendFxApplicationInvoker invoker)
+ {
+ _tenantIdProvider = tenantIdProvider;
+ _invoker = invoker;
+ }
+
+ public void Invoke(Action action)
+ {
+ var correlationId = Guid.NewGuid();
+ TenantId[] tenantIds = _tenantIdProvider.GetActiveDemonstrationTenantIds().Concat(_tenantIdProvider.GetActiveProductionTenantIds()).ToArray();
+ Logger.Debug($"Action will be called in tenants: {string.Join(",", tenantIds.Select(t => t.ToString()))}");
+ foreach (TenantId tenantId in tenantIds)
+ {
+ _invoker.Invoke(action, new SystemIdentity(), tenantId, correlationId);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/BackendFxApplicationTenantExtensions.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/BackendFxApplicationTenantExtensions.cs
deleted file mode 100644
index 100c94a1..00000000
--- a/src/abstractions/Backend.Fx/Environment/MultiTenancy/BackendFxApplicationTenantExtensions.cs
+++ /dev/null
@@ -1,88 +0,0 @@
-using System;
-using System.Linq;
-using System.Reflection;
-using Backend.Fx.Environment.Authentication;
-using Backend.Fx.Logging;
-using Backend.Fx.Patterns.DataGeneration;
-using Backend.Fx.Patterns.DependencyInjection;
-using Backend.Fx.Patterns.EventAggregation.Integration;
-
-namespace Backend.Fx.Environment.MultiTenancy
-{
- public static class BackendFxApplicationTenantExtensions
- {
- private static readonly ILogger Logger = LogManager.Create(typeof(BackendFxApplicationTenantExtensions));
-
- public static void RegisterSeedActionForNewlyCreatedTenants(this IBackendFxApplication application, ITenantService tenantService)
- {
- application.CompositionRoot
- .GetInstance()
- .Subscribe(new DelegateIntegrationEventHandler(tenantCreated =>
- {
- Logger.Info($"Seeding data for recently created {(tenantCreated.IsDemoTenant?"demo ":"")}tenant {tenantCreated.TenantId}");
- try
- {
- var tenantId = new TenantId(tenantCreated.TenantId);
- application.SeedDataForTenant(tenantId, tenantCreated.IsDemoTenant);
- tenantService.ActivateTenant(tenantId);
- }
- catch (Exception ex)
- {
- Logger.Error(ex, $"Seeding data for recently created {(tenantCreated.IsDemoTenant ? "demo " : "")}tenant {tenantCreated.TenantId} failed.");
- }
- }));
- }
-
- public static void SeedDataForAllActiveTenants(this IBackendFxApplication application)
- {
- using (Logger.InfoDuration("Seeding data"))
- {
- var prodTenantIds = application.TenantIdService.GetActiveProductionTenantIds();
- foreach (var prodTenantId in prodTenantIds)
- {
- application.SeedDataForTenant(prodTenantId, false);
- }
-
- var demoTenantIds = application.TenantIdService.GetActiveDemonstrationTenantIds();
- foreach (var demoTenantId in demoTenantIds)
- {
- application.SeedDataForTenant(demoTenantId, true);
- }
- }
- }
-
- private static void SeedDataForTenant(this IBackendFxApplication application, TenantId tenantId, bool isDemoTenant)
- {
- using (Logger.InfoDuration($"Seeding data for tenant {tenantId.Value}"))
- {
- Type[] dataGeneratorTypesToRun;
-
- using (application.BeginScope())
- {
- var dataGenerators = application.CompositionRoot.GetInstances()
- .OrderBy(dg => dg.Priority)
- .Select(dg => dg.GetType());
-
- if (!isDemoTenant)
- {
- dataGenerators = dataGenerators.Where(dg => !typeof(IDemoDataGenerator).IsAssignableFrom(dg));
- }
-
- dataGeneratorTypesToRun = dataGenerators.ToArray();
- }
-
- foreach (var dataGeneratorTypeToRun in dataGeneratorTypesToRun)
- {
- application.Invoke(() =>
- {
- IDataGenerator dataGenerator = application
- .CompositionRoot
- .GetInstances()
- .Single(dg => dg.GetType() == dataGeneratorTypeToRun);
- dataGenerator.Generate();
- }, new SystemIdentity(), tenantId);
- }
- }
- }
- }
-}
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/CurrentTenantIdHolder.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/CurrentTenantIdHolder.cs
index 7898b24a..ede076ae 100644
--- a/src/abstractions/Backend.Fx/Environment/MultiTenancy/CurrentTenantIdHolder.cs
+++ b/src/abstractions/Backend.Fx/Environment/MultiTenancy/CurrentTenantIdHolder.cs
@@ -1,7 +1,7 @@
-namespace Backend.Fx.Environment.MultiTenancy
-{
- using Patterns.DependencyInjection;
+using Backend.Fx.Patterns.DependencyInjection;
+namespace Backend.Fx.Environment.MultiTenancy
+{
public class CurrentTenantIdHolder : CurrentTHolder
{
public static CurrentTenantIdHolder Create(int tenantId)
@@ -34,7 +34,7 @@ protected override string Describe(TenantId instance)
{
return $"TenantId: {instance.Value}";
}
-
+
return "TenantId: null";
}
}
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/ITenantIdService.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/ITenantIdService.cs
deleted file mode 100644
index 6d42c8fd..00000000
--- a/src/abstractions/Backend.Fx/Environment/MultiTenancy/ITenantIdService.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using System.Linq;
-
-namespace Backend.Fx.Environment.MultiTenancy
-{
- public interface ITenantIdService
- {
- TenantId[] GetActiveTenantIds();
- TenantId[] GetActiveDemonstrationTenantIds();
- TenantId[] GetActiveProductionTenantIds();
- }
-
- public class TenantIdService : ITenantIdService
- {
- private readonly ITenantRepository _tenantRepository;
-
- public TenantIdService(ITenantRepository tenantRepository)
- {
- _tenantRepository = tenantRepository;
- }
-
-
- public TenantId[] GetActiveTenantIds()
- {
- return _tenantRepository
- .GetTenants()
- .Where(t => t.State == TenantState.Active)
- .Select(t => new TenantId(t.Id))
- .ToArray();
- }
-
- public TenantId[] GetActiveDemonstrationTenantIds()
- {
- return _tenantRepository
- .GetTenants()
- .Where(t => t.State == TenantState.Active && t.IsDemoTenant)
- .Select(t => new TenantId(t.Id))
- .ToArray();
- }
-
- public TenantId[] GetActiveProductionTenantIds()
- {
- return _tenantRepository
- .GetTenants()
- .Where(t => t.State == TenantState.Active && !t.IsDemoTenant)
- .Select(t => new TenantId(t.Id))
- .ToArray();
- }
- }
-
-}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/ITenantService.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/ITenantService.cs
deleted file mode 100644
index f165eb2f..00000000
--- a/src/abstractions/Backend.Fx/Environment/MultiTenancy/ITenantService.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using System;
-using System.Linq;
-using Backend.Fx.Logging;
-using Backend.Fx.Patterns.EventAggregation.Integration;
-using JetBrains.Annotations;
-
-namespace Backend.Fx.Environment.MultiTenancy
-{
- ///
- /// Encapsulates the management of tenants
- /// Note that this should not use repositories and other building blocks, but access the persistence layer directly
- ///
- public interface ITenantService
- {
- TenantId CreateDemonstrationTenant(string name, string description, string defaultCultureName);
- TenantId CreateProductionTenant(string name, string description, string defaultCultureName);
- void ActivateTenant(TenantId tenantId);
- void DeactivateTenant(TenantId tenantId);
- }
-
- public class TenantService : ITenantService
- {
- private readonly IEventBus _eventBus;
- private readonly ITenantRepository _tenantRepository;
- private static readonly ILogger Logger = LogManager.Create();
-
- public TenantService(IEventBus eventBus, ITenantRepository tenantRepository)
- {
- _eventBus = eventBus;
- _tenantRepository = tenantRepository;
- }
-
- public TenantId CreateDemonstrationTenant(string name, string description, string defaultCultureName)
- {
- Logger.Info($"Creating demonstration tenant: {name}");
- return CreateTenant(name, description, true, defaultCultureName);
- }
-
- public TenantId CreateProductionTenant(string name, string description, string defaultCultureName)
- {
- Logger.Info($"Creating production tenant: {name}");
- return CreateTenant(name, description, false, defaultCultureName);
- }
-
- public void ActivateTenant(TenantId tenantId)
- {
- var tenant = _tenantRepository.GetTenant(tenantId);
- tenant.State = TenantState.Active;
- _tenantRepository.SaveTenant(tenant);
- _eventBus.Publish(new TenantActivated(tenant.Id, tenant.Name, tenant.Description, tenant.IsDemoTenant, tenant.DefaultCultureName));
- }
-
- public void DeactivateTenant(TenantId tenantId)
- {
- var tenant = _tenantRepository.GetTenant(tenantId);
- tenant.State = TenantState.Inactive;
- _tenantRepository.SaveTenant(tenant);
- _eventBus.Publish(new TenantDeactivated(tenant.Id, tenant.Name, tenant.Description, tenant.IsDemoTenant, tenant.DefaultCultureName));
- }
-
- protected virtual TenantId CreateTenant([NotNull] string name, string description, bool isDemo, string defaultCultureName)
- {
- if (string.IsNullOrWhiteSpace(name))
- {
- throw new ArgumentException("Value cannot be null or whitespace.", nameof(name));
- }
-
- if (_tenantRepository.GetTenants().Any(t => t.Name != null && t.Name.ToLowerInvariant() == name.ToLowerInvariant()))
- {
- throw new ArgumentException($"There is already a tenant named {name}");
- }
-
- Tenant tenant = new Tenant(name, description, isDemo, defaultCultureName);
- _tenantRepository.SaveTenant(tenant);
- var tenantId = new TenantId(tenant.Id);
- _eventBus.Publish(new TenantCreated(tenant.Id, tenant.Name, tenant.Description, tenant.IsDemoTenant, tenant.DefaultCultureName));
- return tenantId;
- }
- }
-}
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/MultiTenantApplication.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/MultiTenantApplication.cs
new file mode 100644
index 00000000..7d1de723
--- /dev/null
+++ b/src/abstractions/Backend.Fx/Environment/MultiTenancy/MultiTenantApplication.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Backend.Fx.Patterns.DependencyInjection;
+using Backend.Fx.Patterns.EventAggregation.Integration;
+
+namespace Backend.Fx.Environment.MultiTenancy
+{
+ public class MultiTenantApplication : TenantApplication, IBackendFxApplication
+ {
+ private readonly IBackendFxApplication _application;
+
+ public MultiTenantApplication(IBackendFxApplication application) : base(application)
+ {
+ _application = application;
+ }
+
+ public void Dispose()
+ {
+ _application.Dispose();
+ }
+
+ public IBackendFxApplicationAsyncInvoker AsyncInvoker => _application.AsyncInvoker;
+
+ public ICompositionRoot CompositionRoot => _application.CompositionRoot;
+
+ public IBackendFxApplicationInvoker Invoker => _application.Invoker;
+
+ public IMessageBus MessageBus => _application.MessageBus;
+
+ public bool WaitForBoot(int timeoutMilliSeconds = Int32.MaxValue, CancellationToken cancellationToken = default)
+ {
+ return _application.WaitForBoot(timeoutMilliSeconds, cancellationToken);
+ }
+
+ public async Task Boot(CancellationToken cancellationToken = default)
+ {
+ EnableDataGenerationForNewTenants();
+
+ await _application.Boot(cancellationToken);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/SingleTenantApplication.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/SingleTenantApplication.cs
new file mode 100644
index 00000000..dc5fa344
--- /dev/null
+++ b/src/abstractions/Backend.Fx/Environment/MultiTenancy/SingleTenantApplication.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Backend.Fx.Logging;
+using Backend.Fx.Patterns.DependencyInjection;
+using Backend.Fx.Patterns.EventAggregation.Integration;
+
+namespace Backend.Fx.Environment.MultiTenancy
+{
+ public class SingleTenantApplication : TenantApplication, IBackendFxApplication
+ {
+ private static readonly ILogger Logger = LogManager.Create();
+ private readonly ITenantService _tenantService;
+ private readonly IBackendFxApplication _application;
+ private readonly TenantCreationParameters _tenantCreationParameters;
+
+ public SingleTenantApplication(TenantCreationParameters tenantCreationParameters, ITenantService tenantService, IBackendFxApplication application) : base(application)
+ {
+ _tenantService = tenantService;
+ _application = application;
+ _tenantCreationParameters = tenantCreationParameters;
+ }
+
+ public void Dispose()
+ {
+ _application.Dispose();
+ }
+
+ public IBackendFxApplicationAsyncInvoker AsyncInvoker => _application.AsyncInvoker;
+
+ public ICompositionRoot CompositionRoot => _application.CompositionRoot;
+
+ public IBackendFxApplicationInvoker Invoker => _application.Invoker;
+
+ public IMessageBus MessageBus => _application.MessageBus;
+
+ public TenantId TenantId { get; private set; }
+
+ public bool WaitForBoot(int timeoutMilliSeconds = Int32.MaxValue, CancellationToken cancellationToken = default)
+ {
+ return _application.WaitForBoot(timeoutMilliSeconds, cancellationToken);
+ }
+
+ public async Task Boot(CancellationToken cancellationToken = default)
+ {
+ EnableDataGenerationForNewTenants();
+
+ await _application.Boot(cancellationToken);
+
+ Logger.Info($"Ensuring existence of single tenant {_tenantCreationParameters.Name}");
+ TenantId = _tenantService.GetActiveTenantIds().SingleOrDefault()
+ ?? _tenantService.CreateTenant(_tenantCreationParameters);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/Tenant.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/Tenant.cs
index 28417a63..f038f099 100644
--- a/src/abstractions/Backend.Fx/Environment/MultiTenancy/Tenant.cs
+++ b/src/abstractions/Backend.Fx/Environment/MultiTenancy/Tenant.cs
@@ -1,11 +1,9 @@
-namespace Backend.Fx.Environment.MultiTenancy
-{
- using System;
- using System.ComponentModel.DataAnnotations;
- using System.ComponentModel.DataAnnotations.Schema;
- using System.Globalization;
- using JetBrains.Annotations;
+using System;
+using System.ComponentModel.DataAnnotations;
+using JetBrains.Annotations;
+namespace Backend.Fx.Environment.MultiTenancy
+{
///
/// Represents a tenant in the application
///
@@ -13,44 +11,32 @@ public class Tenant
{
[UsedImplicitly]
private Tenant()
- { }
+ {
+ }
- public Tenant([NotNull] string name, string description, bool isDemoTenant, string defaultCultureName)
+ public Tenant([NotNull] string name, string description, bool isDemoTenant)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(name));
Name = name;
Description = description;
IsDemoTenant = isDemoTenant;
- DefaultCultureName = defaultCultureName;
- State = TenantState.Created;
+ State = TenantState.Active;
}
-
- [Key]
- public int Id { get; set; }
- [Required]
- public string Name { get; set; }
+ [Key] public int Id { get; set; }
+
+ [Required] public string Name { get; set; }
public string Description { get; set; }
public bool IsDemoTenant { get; set; }
-
- public TenantState State { get; set; }
-
- public string DefaultCultureName { get; set; }
- [NotMapped]
- public CultureInfo DefaultCulture
- {
- get => new CultureInfo(DefaultCultureName);
- set => DefaultCultureName = value.Name;
- }
+ public TenantState State { get; set; }
}
public enum TenantState
{
- Created = 0,
Active = 2,
Inactive = -1
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantActivated.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantActivated.cs
index 26f7dbc8..d4bc387d 100644
--- a/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantActivated.cs
+++ b/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantActivated.cs
@@ -2,8 +2,8 @@
{
public class TenantActivated : TenantStatusChanged
{
- public TenantActivated(int tenantId, string name, string description, bool isDemoTenant, string defaultCultureName)
- : base(tenantId, name, description, isDemoTenant, defaultCultureName)
+ public TenantActivated(int tenantId, string name, string description, bool isDemoTenant)
+ : base(tenantId, name, description, isDemoTenant)
{
}
}
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantApplication.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantApplication.cs
new file mode 100644
index 00000000..32ce9ac8
--- /dev/null
+++ b/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantApplication.cs
@@ -0,0 +1,39 @@
+using System;
+using Backend.Fx.Logging;
+using Backend.Fx.Patterns.DataGeneration;
+using Backend.Fx.Patterns.DependencyInjection;
+using Backend.Fx.Patterns.EventAggregation.Integration;
+
+namespace Backend.Fx.Environment.MultiTenancy
+{
+ public abstract class TenantApplication
+ {
+ private static readonly ILogger Logger = LogManager.Create();
+
+ private readonly IBackendFxApplication _application;
+ private readonly DataGenerationContext _dataGenerationContext;
+
+
+ protected TenantApplication(IBackendFxApplication application)
+ {
+ _application = application;
+ _dataGenerationContext = new DataGenerationContext(_application.CompositionRoot, _application.Invoker);
+ }
+
+ protected void EnableDataGenerationForNewTenants()
+ {
+ _application.MessageBus.Subscribe(new DelegateIntegrationMessageHandler(tenantCreated =>
+ {
+ Logger.Info($"Seeding data for recently activated {(tenantCreated.IsDemoTenant ? "demo " : "")}tenant {tenantCreated.TenantId}");
+ try
+ {
+ _dataGenerationContext.SeedDataForTenant(new TenantId(tenantCreated.TenantId), tenantCreated.IsDemoTenant);
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(ex, $"Seeding data for recently activated {(tenantCreated.IsDemoTenant ? "demo " : "")}tenant {tenantCreated.TenantId} failed.");
+ }
+ }));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantCreated.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantCreated.cs
deleted file mode 100644
index cd026d2a..00000000
--- a/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantCreated.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-namespace Backend.Fx.Environment.MultiTenancy
-{
- public class TenantCreated : TenantStatusChanged
- {
- public TenantCreated(int tenantId, string name, string description, bool isDemoTenant, string defaultCultureName)
- : base(tenantId, name, description, isDemoTenant, defaultCultureName)
- {
- }
- }
-}
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantCreationParameters.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantCreationParameters.cs
new file mode 100644
index 00000000..850646fa
--- /dev/null
+++ b/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantCreationParameters.cs
@@ -0,0 +1,20 @@
+namespace Backend.Fx.Environment.MultiTenancy
+{
+ public class TenantCreationParameters
+ {
+ public TenantCreationParameters()
+ {
+ }
+
+ public TenantCreationParameters(string name, string description, bool isDemonstrationTenant)
+ {
+ Name = name;
+ Description = description;
+ IsDemonstrationTenant = isDemonstrationTenant;
+ }
+
+ public bool IsDemonstrationTenant { get; set; }
+ public string Name { get; set; } = "Tenant";
+ public string Description { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantDeactivated.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantDeactivated.cs
index 818a716f..eb2399d9 100644
--- a/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantDeactivated.cs
+++ b/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantDeactivated.cs
@@ -1,23 +1,10 @@
-using Backend.Fx.Patterns.EventAggregation.Integration;
-
-namespace Backend.Fx.Environment.MultiTenancy
+namespace Backend.Fx.Environment.MultiTenancy
{
- public class TenantDeactivated : IntegrationEvent
+ public class TenantDeactivated : TenantStatusChanged
{
- public TenantDeactivated(int tenantId, string name, string description, bool isDemoTenant, string defaultCultureName) : base(tenantId)
+ public TenantDeactivated(int tenantId, string name, string description, bool isDemoTenant)
+ : base(tenantId, name, description, isDemoTenant)
{
- Name = name;
- Description = description;
- IsDemoTenant = isDemoTenant;
- DefaultCultureName = defaultCultureName;
}
-
- public string Name { get; }
-
- public string Description { get; }
-
- public bool IsDemoTenant { get; }
-
- public string DefaultCultureName { get; }
}
}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantId.cs b/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantId.cs
index 48cd1c17..46b43d2c 100644
--- a/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantId.cs
+++ b/src/abstractions/Backend.Fx/Environment/MultiTenancy/TenantId.cs
@@ -1,11 +1,10 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Backend.Fx.BuildingBlocks;
namespace Backend.Fx.Environment.MultiTenancy
{
- using System;
- using System.Diagnostics;
- using BuildingBlocks;
-
[DebuggerDisplay("{" + nameof(DebuggerDisplay) + "}")]
public class TenantId : ValueObject
{
@@ -38,7 +37,6 @@ protected string DebuggerDisplay
{
get
{
-
if (HasValue)
{
return $"TenantId: {Value}";
@@ -48,6 +46,11 @@ protected string DebuggerDisplay
}
}
+ public override string ToString()
+ {
+ return _id?.ToString() ?? "NULL";
+ }
+
protected override IEnumerable
/// The handler type
- /// Th event name to subscribe to. (Should be Type.FullName to avoid namespace collisions)
- void Subscribe(string eventName)
- where THandler : IIntegrationEventHandler;
+ /// The event name to subscribe to.
+ void Subscribe(string messageName)
+ where THandler : IIntegrationMessageHandler;
///
/// Subscribes to an integration event with a generically typed event handler
@@ -31,24 +38,26 @@ void Subscribe(string eventName)
/// The handler type
/// The event type to subscribe to
void Subscribe()
- where THandler : IIntegrationEventHandler
+ where THandler : IIntegrationMessageHandler
where TEvent : IIntegrationEvent;
///
/// Subscribes to an integration event with a singleton instance event handler
///
/// The event type to subscribe to
- void Subscribe(IIntegrationEventHandler handler)
+ void Subscribe(IIntegrationMessageHandler handler)
where TEvent : IIntegrationEvent;
- void Unsubscribe(string eventName)
- where THandler : IIntegrationEventHandler;
+ void Unsubscribe(string messageName)
+ where THandler : IIntegrationMessageHandler;
void Unsubscribe()
- where THandler : IIntegrationEventHandler
+ where THandler : IIntegrationMessageHandler
where TEvent : IIntegrationEvent;
- void Unsubscribe(IIntegrationEventHandler handler)
+ void Unsubscribe(IIntegrationMessageHandler handler)
where TEvent : IIntegrationEvent;
+
+ void ProvideInvoker(IBackendFxApplicationInvoker invoker);
}
}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/IMessageNameProvider.cs b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/IMessageNameProvider.cs
new file mode 100644
index 00000000..cd7883a0
--- /dev/null
+++ b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/IMessageNameProvider.cs
@@ -0,0 +1,15 @@
+using System;
+using JetBrains.Annotations;
+
+namespace Backend.Fx.Patterns.EventAggregation.Integration
+{
+ public interface IMessageNameProvider
+ {
+ [NotNull]
+ string GetMessageName();
+ [NotNull]
+ string GetMessageName(Type t);
+ [NotNull]
+ string GetMessageName(IIntegrationEvent integrationEvent);
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/ISubscription.cs b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/ISubscription.cs
index 8534fcfa..91ca9a39 100644
--- a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/ISubscription.cs
+++ b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/ISubscription.cs
@@ -1,8 +1,10 @@
-namespace Backend.Fx.Patterns.EventAggregation.Integration
+using Backend.Fx.Patterns.DependencyInjection;
+
+namespace Backend.Fx.Patterns.EventAggregation.Integration
{
public interface ISubscription
{
- void Process(string eventName, EventProcessingContext context);
+ void Process(IInstanceProvider instanceProvider, EventProcessingContext context);
bool Matches(object handler);
}
}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/InMemoryEventBus.cs b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/InMemoryMessageBus.cs
similarity index 61%
rename from src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/InMemoryEventBus.cs
rename to src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/InMemoryMessageBus.cs
index 6b104259..13f558dc 100644
--- a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/InMemoryEventBus.cs
+++ b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/InMemoryMessageBus.cs
@@ -1,34 +1,30 @@
-using Backend.Fx.Patterns.DependencyInjection;
-
-namespace Backend.Fx.Patterns.EventAggregation.Integration
+namespace Backend.Fx.Patterns.EventAggregation.Integration
{
using System;
using System.Threading.Tasks;
using Environment.MultiTenancy;
- public class InMemoryEventBus : EventBus
+ public class InMemoryMessageBus : MessageBus
{
- public InMemoryEventBus(IBackendFxApplication application)
- : base(application)
+ public override void Connect()
{
}
- public override void Connect()
- { }
-
- protected override Task PublishOnEventBus(IIntegrationEvent integrationEvent)
+ protected override Task PublishOnMessageBus(IIntegrationEvent integrationEvent)
{
- Task.Run(() => Process(integrationEvent.GetType().FullName, new InMemoryProcessingContext(integrationEvent)));
-
+ Task.Run(() => Process(MessageNameProvider.GetMessageName(integrationEvent), new InMemoryProcessingContext(integrationEvent)));
+
// the returning Task is about publishing the event, not processing!
return Task.CompletedTask;
}
- protected override void Subscribe(string eventName)
- { }
+ protected override void Subscribe(string messageName)
+ {
+ }
- protected override void Unsubscribe(string eventName)
- { }
+ protected override void Unsubscribe(string messageName)
+ {
+ }
private class InMemoryProcessingContext : EventProcessingContext
{
@@ -50,4 +46,4 @@ public override IIntegrationEvent GetTypedEvent(Type eventType)
}
}
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/IntegrationEvent.cs b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/IntegrationEvent.cs
index 5e25bb03..91d6a721 100644
--- a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/IntegrationEvent.cs
+++ b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/IntegrationEvent.cs
@@ -1,5 +1,4 @@
using System;
-using Backend.Fx.Patterns.DependencyInjection;
namespace Backend.Fx.Patterns.EventAggregation.Integration
{
@@ -12,7 +11,7 @@ public interface IIntegrationEvent
}
///
- /// Events that should be handled in a separate context. Might be persisted as well using an external event bus.
+ /// Events that should be handled in a separate context. Might be persisted as well using an external message bus.
/// See https://blogs.msdn.microsoft.com/cesardelatorre/2017/02/07/domain-events-vs-integration-events-in-domain-driven-design-and-microservices-architectures/
///
public abstract class IntegrationEvent : IIntegrationEvent
@@ -22,9 +21,9 @@ public abstract class IntegrationEvent : IIntegrationEvent
public DateTime CreationDate { get; } = DateTime.UtcNow;
public int TenantId { get; }
-
+
public Guid CorrelationId { get; private set; }
-
+
internal void SetCorrelationId(Guid correlationId)
{
CorrelationId = correlationId;
@@ -35,4 +34,4 @@ protected IntegrationEvent(int tenantId)
TenantId = tenantId;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/MessageBus.cs b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/MessageBus.cs
new file mode 100644
index 00000000..d2dd3cc6
--- /dev/null
+++ b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/MessageBus.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Backend.Fx.Environment.Authentication;
+using Backend.Fx.Logging;
+using Backend.Fx.Patterns.DependencyInjection;
+
+namespace Backend.Fx.Patterns.EventAggregation.Integration
+{
+ public abstract class MessageBus : IMessageBus
+ {
+ private static readonly ILogger Logger = LogManager.Create();
+
+ ///
+ /// Holds the registered handlers.
+ /// Each event type name (key) matches to various subscriptions
+ ///
+ private readonly ConcurrentDictionary> _subscriptions = new ConcurrentDictionary>();
+
+ private IBackendFxApplicationInvoker _invoker;
+
+ public IMessageNameProvider MessageNameProvider { get; } = new DefaultMessageNameProvider();
+ public abstract void Connect();
+
+ public void ProvideInvoker(IBackendFxApplicationInvoker invoker)
+ {
+ _invoker = invoker;
+ }
+
+ public Task Publish(IIntegrationEvent integrationEvent)
+ {
+ return PublishOnMessageBus(integrationEvent);
+ }
+
+ protected abstract Task PublishOnMessageBus(IIntegrationEvent integrationEvent);
+
+
+ ///
+ public void Subscribe(string messageName) where THandler : IIntegrationMessageHandler
+ {
+ Logger.Info($"Subscribing to {messageName}");
+ EnsureInvoker();
+ var subscription = new DynamicSubscription(typeof(THandler));
+ _subscriptions.AddOrUpdate(messageName,
+ s => new List {subscription},
+ (s, list) =>
+ {
+ list.Add(subscription);
+ return list;
+ });
+ Subscribe(messageName);
+ }
+
+ ///
+ public void Subscribe() where THandler : IIntegrationMessageHandler where TEvent : IIntegrationEvent
+ {
+ string eventName = MessageNameProvider.GetMessageName();
+ Logger.Info($"Subscribing to {eventName}");
+ EnsureInvoker();
+ var subscription = new TypedSubscription(typeof(THandler));
+ _subscriptions.AddOrUpdate(eventName,
+ s => new List {subscription},
+ (s, list) =>
+ {
+ list.Add(subscription);
+ return list;
+ });
+ Subscribe(eventName);
+ }
+
+ public void Subscribe(IIntegrationMessageHandler handler)
+ where TEvent : IIntegrationEvent
+ {
+ string eventName = MessageNameProvider.GetMessageName();
+ Logger.Info($"Subscribing to {eventName}");
+ EnsureInvoker();
+ var subscription = new SingletonSubscription(handler);
+ _subscriptions.AddOrUpdate(eventName,
+ s => new List {subscription},
+ (s, list) =>
+ {
+ list.Add(subscription);
+ return list;
+ });
+ Subscribe(eventName);
+ }
+
+ public void Unsubscribe(string messageName) where THandler : IIntegrationMessageHandler
+ {
+ Logger.Info($"Unsubscribing from {messageName}");
+ if (_subscriptions.TryGetValue(messageName, out var handlers))
+ {
+ handlers.RemoveAll(t => t.Matches(typeof(THandler)));
+ }
+
+ Unsubscribe(messageName);
+ }
+
+ public void Unsubscribe() where THandler : IIntegrationMessageHandler where TEvent : IIntegrationEvent
+ {
+ string eventName = MessageNameProvider.GetMessageName();
+ Logger.Info($"Unsubscribing from {eventName}");
+ if (_subscriptions.TryGetValue(eventName, out var handlers))
+ {
+ handlers.RemoveAll(t => t.Matches(typeof(THandler)));
+ }
+
+ Unsubscribe(eventName);
+ }
+
+ public void Unsubscribe(IIntegrationMessageHandler handler) where TEvent : IIntegrationEvent
+ {
+ string eventName = MessageNameProvider.GetMessageName();
+ Logger.Info($"Unsubscribing from {eventName}");
+ if (_subscriptions.TryGetValue(eventName, out var handlers))
+ {
+ handlers.RemoveAll(t => t.Matches(handler));
+ }
+
+ Unsubscribe(eventName);
+ }
+
+ protected abstract void Subscribe(string messageName);
+ protected abstract void Unsubscribe(string messageName);
+
+ protected void Process(string messageName, EventProcessingContext context)
+ {
+ Logger.Info($"Processing a {messageName} message");
+ EnsureInvoker();
+
+ if (_subscriptions.TryGetValue(messageName, out List subscriptions))
+ {
+ foreach (ISubscription subscription in subscriptions)
+ {
+ try
+ {
+ _invoker.Invoke(
+ instanceProvider => subscription.Process(instanceProvider, context),
+ new SystemIdentity(),
+ context.TenantId,
+ context.CorrelationId);
+ }
+ catch (Exception ex)
+ {
+ Logger.Warn(ex, $"Processing a {messageName} message failed");
+ throw;
+ }
+ }
+ }
+ else
+ {
+ Logger.Info($"No handler registered. Ignoring {messageName} event");
+ }
+ }
+
+ private void EnsureInvoker()
+ {
+ if (_invoker == null)
+ {
+ throw new InvalidOperationException("Before using the message bus you have to provide the application invoker by calling ProvideInvoker()");
+ }
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ private class DefaultMessageNameProvider : IMessageNameProvider
+ {
+ public string GetMessageName()
+ {
+ return GetMessageName(typeof(T));
+ }
+
+ public string GetMessageName(Type t)
+ {
+ var messageName = t.Name ?? throw new ArgumentException("Type name is null!");
+ return messageName;
+ }
+
+ public string GetMessageName(IIntegrationEvent integrationEvent)
+ {
+ return GetMessageName(integrationEvent.GetType());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/EventBusScope.cs b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/MessageBusScope.cs
similarity index 50%
rename from src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/EventBusScope.cs
rename to src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/MessageBusScope.cs
index ac981196..70880bef 100644
--- a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/EventBusScope.cs
+++ b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/MessageBusScope.cs
@@ -1,32 +1,37 @@
-namespace Backend.Fx.Patterns.EventAggregation.Integration
+using Backend.Fx.Patterns.DependencyInjection;
+
+namespace Backend.Fx.Patterns.EventAggregation.Integration
{
using System.Collections.Concurrent;
using System.Threading.Tasks;
-
- public interface IEventBusScope
- {
+
+ public interface IMessageBusScope
+ {
///
/// Enqueue an event to be raised later.
- /// Intention is to let events bubble up after an operation has terminated, e.g. when a wrapping
- /// unit of work has completed.
+ /// Intention is to let events bubble up after an operation has terminated
///
///
void Publish(IIntegrationEvent integrationEvent);
+
Task RaiseEvents();
}
- public class EventBusScope : IEventBusScope
+ public class MessageBusScope : IMessageBusScope
{
private readonly ConcurrentQueue _integrationEvents = new ConcurrentQueue();
- private readonly IEventBus _eventBus;
+ private readonly IMessageBus _messageBus;
+ private readonly ICurrentTHolder _correlationHolder;
- public EventBusScope(IEventBus eventBus)
+ public MessageBusScope(IMessageBus messageBus, ICurrentTHolder correlationHolder)
{
- _eventBus = eventBus;
+ _messageBus = messageBus;
+ _correlationHolder = correlationHolder;
}
-
- void IEventBusScope.Publish(IIntegrationEvent integrationEvent)
+
+ void IMessageBusScope.Publish(IIntegrationEvent integrationEvent)
{
+ ((IntegrationEvent) integrationEvent).SetCorrelationId(_correlationHolder.Current.Id);
_integrationEvents.Enqueue(integrationEvent);
}
@@ -34,9 +39,8 @@ public async Task RaiseEvents()
{
while (_integrationEvents.TryDequeue(out IIntegrationEvent integrationEvent))
{
- await _eventBus.Publish(integrationEvent);
+ await _messageBus.Publish(integrationEvent);
}
}
}
-}
-
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/SingletonSubscription.cs b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/SingletonSubscription.cs
index 0757432f..424a605e 100644
--- a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/SingletonSubscription.cs
+++ b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/SingletonSubscription.cs
@@ -1,22 +1,23 @@
using Backend.Fx.Logging;
+using Backend.Fx.Patterns.DependencyInjection;
namespace Backend.Fx.Patterns.EventAggregation.Integration
{
- public class SingletonSubscription: ISubscription where TEvent : IIntegrationEvent
+ public class SingletonSubscription : ISubscription where TEvent : IIntegrationEvent
{
private static readonly ILogger Logger = LogManager.Create>();
- private readonly IIntegrationEventHandler _handler;
+ private readonly IIntegrationMessageHandler _handler;
- public SingletonSubscription(IIntegrationEventHandler handler)
+ public SingletonSubscription(IIntegrationMessageHandler handler)
{
_handler = handler;
}
- public void Process(string eventName, EventProcessingContext context)
+ public void Process(IInstanceProvider instanceProvider, EventProcessingContext context)
{
using (Logger.InfoDuration($"Invoking subscribed handler {_handler.GetType().Name}"))
{
- _handler.Handle((TEvent)context.GetTypedEvent(typeof(TEvent)));
+ _handler.Handle((TEvent) context.GetTypedEvent(typeof(TEvent)));
}
}
diff --git a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/TypedSubscription.cs b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/TypedSubscription.cs
index a39fb885..c644da9c 100644
--- a/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/TypedSubscription.cs
+++ b/src/abstractions/Backend.Fx/Patterns/EventAggregation/Integration/TypedSubscription.cs
@@ -11,35 +11,33 @@ namespace Backend.Fx.Patterns.EventAggregation.Integration
public class TypedSubscription : ISubscription
{
private static readonly ILogger Logger = LogManager.Create();
- private readonly IBackendFxApplication _application;
private readonly Type _handlerType;
- public TypedSubscription(IBackendFxApplication application, Type handlerType)
+ public TypedSubscription(Type handlerType)
{
- _application = application;
_handlerType = handlerType;
}
- public void Process(string eventName, EventProcessingContext context)
+ public void Process(IInstanceProvider instanceProvider, EventProcessingContext context)
{
- Type interfaceType = _handlerType.GetInterfaces().First(x => x.GetTypeInfo().IsGenericType && x.GetGenericTypeDefinition() == typeof(IIntegrationEventHandler<>));
- var eventType = interfaceType.GetGenericArguments().Single(t => typeof(IIntegrationEvent).IsAssignableFrom(t));
- var integrationEvent = context.GetTypedEvent(eventType);
- MethodInfo handleMethod = _handlerType.GetRuntimeMethod("Handle", new[] { eventType });
- Debug.Assert(handleMethod != null, $"No method with signature `Handle({eventName} event)` found on {_handlerType.Name}");
+ Type interfaceType = _handlerType.GetInterfaces().First(x => x.GetTypeInfo().IsGenericType && x.GetGenericTypeDefinition() == typeof(IIntegrationMessageHandler<>));
+ Type eventType = interfaceType.GetGenericArguments().Single(t => typeof(IIntegrationEvent).IsAssignableFrom(t));
+ IIntegrationEvent integrationEvent = context.GetTypedEvent(eventType);
+ MethodInfo handleMethod = _handlerType.GetRuntimeMethod("Handle", new[] {eventType});
+ Debug.Assert(handleMethod != null, $"No method with signature `Handle({eventType.Name} event)` found on {_handlerType.Name}");
Logger.Info($"Getting subscribed handler instance of type {_handlerType.Name}");
- object handlerInstance = _application.CompositionRoot.GetInstance(_handlerType);
+ object handlerInstance = instanceProvider.GetInstance(_handlerType);
using (Logger.InfoDuration($"Invoking subscribed handler {_handlerType.GetDetailedTypeName()}"))
{
- handleMethod.Invoke(handlerInstance, new object[] { integrationEvent });
+ handleMethod.Invoke(handlerInstance, new object[] {integrationEvent});
}
}
public bool Matches(object handler)
{
- return (Type)handler == _handlerType;
+ return (Type) handler == _handlerType;
}
}
}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/IdGeneration/HiLoIdGenerator.cs b/src/abstractions/Backend.Fx/Patterns/IdGeneration/HiLoIdGenerator.cs
index 37cd55a8..8c2b76bd 100644
--- a/src/abstractions/Backend.Fx/Patterns/IdGeneration/HiLoIdGenerator.cs
+++ b/src/abstractions/Backend.Fx/Patterns/IdGeneration/HiLoIdGenerator.cs
@@ -1,8 +1,8 @@
+using System.Threading;
+using Backend.Fx.Logging;
+
namespace Backend.Fx.Patterns.IdGeneration
{
- using System.Threading;
- using Logging;
-
public abstract class HiLoIdGenerator : IIdGenerator
{
private static readonly ILogger Logger = LogManager.Create();
diff --git a/src/abstractions/Backend.Fx/Patterns/IdGeneration/IIdGenerator.cs b/src/abstractions/Backend.Fx/Patterns/IdGeneration/IIdGenerator.cs
index ba1063a4..5c59c6d2 100644
--- a/src/abstractions/Backend.Fx/Patterns/IdGeneration/IIdGenerator.cs
+++ b/src/abstractions/Backend.Fx/Patterns/IdGeneration/IIdGenerator.cs
@@ -4,4 +4,4 @@ public interface IIdGenerator
{
int NextId();
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/IdGeneration/ISequence.cs b/src/abstractions/Backend.Fx/Patterns/IdGeneration/ISequence.cs
index b2f977a5..02a4516b 100644
--- a/src/abstractions/Backend.Fx/Patterns/IdGeneration/ISequence.cs
+++ b/src/abstractions/Backend.Fx/Patterns/IdGeneration/ISequence.cs
@@ -6,4 +6,4 @@ public interface ISequence
int GetNextValue();
int Increment { get; }
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/IdGeneration/SequenceHiLoIdGenerator.cs b/src/abstractions/Backend.Fx/Patterns/IdGeneration/SequenceHiLoIdGenerator.cs
index c9cd0510..693bccbc 100644
--- a/src/abstractions/Backend.Fx/Patterns/IdGeneration/SequenceHiLoIdGenerator.cs
+++ b/src/abstractions/Backend.Fx/Patterns/IdGeneration/SequenceHiLoIdGenerator.cs
@@ -3,7 +3,7 @@
public abstract class SequenceHiLoIdGenerator : HiLoIdGenerator, IEntityIdGenerator
{
private readonly ISequence _sequence;
-
+
protected SequenceHiLoIdGenerator(ISequence sequence)
{
_sequence = sequence;
diff --git a/src/abstractions/Backend.Fx/Patterns/Jobs/IJob.cs b/src/abstractions/Backend.Fx/Patterns/Jobs/IJob.cs
index afbfbb91..552e21eb 100644
--- a/src/abstractions/Backend.Fx/Patterns/Jobs/IJob.cs
+++ b/src/abstractions/Backend.Fx/Patterns/Jobs/IJob.cs
@@ -7,4 +7,4 @@ public interface IJob
{
void Run();
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/Transactions/ReadonlyDecorator.cs b/src/abstractions/Backend.Fx/Patterns/Transactions/ReadonlyDecorator.cs
deleted file mode 100644
index 0c69a3ee..00000000
--- a/src/abstractions/Backend.Fx/Patterns/Transactions/ReadonlyDecorator.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Data;
-using Backend.Fx.Logging;
-
-namespace Backend.Fx.Patterns.Transactions
-{
- public class ReadonlyDecorator : ITransactionContext
- {
- private static readonly ILogger Logger = LogManager.Create();
-
- private readonly ITransactionContext _transactionContext;
-
- public ReadonlyDecorator(ITransactionContext transactionContext)
- {
- Logger.Debug("Making this transaction context readonly");
- _transactionContext = transactionContext;
- }
-
- public void BeginTransaction() => _transactionContext.BeginTransaction();
-
- public void CommitTransaction()
- {
- Logger.Debug("Committing transaction is intercepted and replaced with rollback transaction to ensure readonly behavior");
- RollbackTransaction();
- }
-
- public void RollbackTransaction() => _transactionContext.RollbackTransaction();
-
- public void SetIsolationLevel(IsolationLevel isolationLevel)
- {
- _transactionContext.SetIsolationLevel(isolationLevel);
- }
-
-
- public IDbTransaction CurrentTransaction => _transactionContext.CurrentTransaction;
- }
-}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/Transactions/TransactionContext.cs b/src/abstractions/Backend.Fx/Patterns/Transactions/TransactionContext.cs
deleted file mode 100644
index 4b79ef78..00000000
--- a/src/abstractions/Backend.Fx/Patterns/Transactions/TransactionContext.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-using System;
-using System.Data;
-using Backend.Fx.Logging;
-
-namespace Backend.Fx.Patterns.Transactions
-{
- ///
- /// wraps an underlying database transaction. In combination with a injection container, access to
- /// the current transaction can be gained by means of this interface.
- ///
- public interface ITransactionContext
- {
- IDbTransaction CurrentTransaction { get; }
- void BeginTransaction();
- void CommitTransaction();
- void RollbackTransaction();
- void SetIsolationLevel(IsolationLevel isolationLevel);
- }
-
- public class TransactionContext : ITransactionContext, IDisposable
- {
- private static readonly ILogger Logger = LogManager.Create();
- private readonly bool _shouldHandleConnectionState;
- private IsolationLevel _isolationLevel = IsolationLevel.Unspecified;
- private IDisposable _transactionLifetimeLogger;
-
- public TransactionContext(IDbConnection connection)
- {
- Connection = connection;
- ConnectionState state = Connection.State;
- switch (state)
- {
- case ConnectionState.Closed:
- _shouldHandleConnectionState = true;
- break;
- case ConnectionState.Open:
- _shouldHandleConnectionState = false;
- break;
- default:
- throw new InvalidOperationException($"A connection provided to the TransactionContext must either be closed or open, but must not be {state}");
- }
- }
-
- public IDbConnection Connection { get; }
-
- public IDbTransaction CurrentTransaction { get; private set; }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- public void BeginTransaction()
- {
- if (_shouldHandleConnectionState)
- {
- Logger.Debug("Opening connection");
- Connection.Open();
- }
-
- Logger.Debug("Beginning transaction");
- CurrentTransaction = Connection.BeginTransaction(_isolationLevel);
- _transactionLifetimeLogger = Logger.DebugDuration("Transaction open");
- }
-
- public void CommitTransaction()
- {
- Logger.Debug("Committing transaction");
- CurrentTransaction.Commit();
- CurrentTransaction.Dispose();
- CurrentTransaction = null;
- _transactionLifetimeLogger?.Dispose();
- _transactionLifetimeLogger = null;
- if (_shouldHandleConnectionState)
- {
- Logger.Debug("Closing connection");
- Connection.Close();
- }
- }
-
- public void RollbackTransaction()
- {
- Logger.Debug("rolling back transaction");
- CurrentTransaction.Rollback();
- CurrentTransaction.Dispose();
- CurrentTransaction = null;
- _transactionLifetimeLogger?.Dispose();
- _transactionLifetimeLogger = null;
- if (_shouldHandleConnectionState)
- {
- Connection.Close();
- }
- }
-
- public void SetIsolationLevel(IsolationLevel isolationLevel)
- {
- if (CurrentTransaction != null)
- {
- throw new InvalidOperationException("Isolation level cannot be changed after the transaction has been started");
- }
-
- _isolationLevel = isolationLevel;
- }
-
- private void Dispose(bool disposing)
- {
- if (disposing)
- {
- try
- {
- CurrentTransaction?.Dispose();
- CurrentTransaction = null;
- if (_shouldHandleConnectionState && Connection.State == ConnectionState.Open)
- {
- Connection.Close();
- }
- }
- catch (Exception ex)
- {
- Logger.Error(ex, "Dispose failed");
- }
-
- _transactionLifetimeLogger?.Dispose();
- CurrentTransaction?.Dispose();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/UnitOfWork/DbConnectionDecorator.cs b/src/abstractions/Backend.Fx/Patterns/UnitOfWork/DbConnectionDecorator.cs
deleted file mode 100644
index a07eb772..00000000
--- a/src/abstractions/Backend.Fx/Patterns/UnitOfWork/DbConnectionDecorator.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using System.Data;
-using System.Security.Principal;
-using Backend.Fx.Patterns.DependencyInjection;
-
-namespace Backend.Fx.Patterns.UnitOfWork
-{
- ///
- /// Enriches the unit of work to open and close a database connection during lifetime
- ///
- public class DbConnectionDecorator : IUnitOfWork
- {
- public DbConnectionDecorator(IDbConnection dbConnection, IUnitOfWork unitOfWork)
- {
- DbConnection = dbConnection;
- UnitOfWork = unitOfWork;
- }
-
- public IUnitOfWork UnitOfWork { get; }
-
- public IDbConnection DbConnection { get; }
-
- public ICurrentTHolder IdentityHolder => UnitOfWork.IdentityHolder;
-
- public void Begin()
- {
- DbConnection.Open();
- UnitOfWork.Begin();
- }
-
- public void Complete()
- {
- UnitOfWork.Complete();
- DbConnection.Close();
- }
- }
-}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/UnitOfWork/DbTransactionDecorator.cs b/src/abstractions/Backend.Fx/Patterns/UnitOfWork/DbTransactionDecorator.cs
deleted file mode 100644
index 4c80beb4..00000000
--- a/src/abstractions/Backend.Fx/Patterns/UnitOfWork/DbTransactionDecorator.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System.Security.Principal;
-using Backend.Fx.Patterns.DependencyInjection;
-using Backend.Fx.Patterns.Transactions;
-
-namespace Backend.Fx.Patterns.UnitOfWork
-{
- ///
- /// Enriches the unit of work to use a database transaction during lifetime. The transaction gets started, before IUnitOfWork.Begin()
- /// is being called and gets committed after IUnitOfWork.Complete() is being called.
- ///
- public class DbTransactionDecorator : IUnitOfWork
- {
- public DbTransactionDecorator(ITransactionContext transactionContext, IUnitOfWork unitOfWork)
- {
- TransactionContext = transactionContext;
- UnitOfWork = unitOfWork;
- }
-
- public IUnitOfWork UnitOfWork { get; }
-
- public ITransactionContext TransactionContext { get; }
-
- public ICurrentTHolder IdentityHolder => UnitOfWork.IdentityHolder;
-
- public virtual void Begin()
- {
- TransactionContext.BeginTransaction();
- UnitOfWork.Begin();
- }
-
- public virtual void Complete()
- {
- UnitOfWork.Complete();
- TransactionContext.CommitTransaction();
- }
- }
-}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/Patterns/UnitOfWork/IUnitOfWork.cs b/src/abstractions/Backend.Fx/Patterns/UnitOfWork/IUnitOfWork.cs
deleted file mode 100644
index 962422f6..00000000
--- a/src/abstractions/Backend.Fx/Patterns/UnitOfWork/IUnitOfWork.cs
+++ /dev/null
@@ -1,87 +0,0 @@
-namespace Backend.Fx.Patterns.UnitOfWork
-{
- using System;
- using System.Security.Principal;
- using DependencyInjection;
- using Environment.DateAndTime;
- using EventAggregation.Domain;
- using EventAggregation.Integration;
- using Extensions;
- using Logging;
-
- ///
- /// Maintains a list of objects affected by a business transaction and coordinates the writing out of changes and the resolution of concurrency problems.
- ///
- public interface IUnitOfWork
- {
- void Begin();
- void Complete();
- ICurrentTHolder IdentityHolder { get; }
- }
-
- public abstract class UnitOfWork : IUnitOfWork, ICanFlush
- {
- private static readonly ILogger Logger = LogManager.Create();
- private static int _index;
- private readonly int _instanceId = _index++;
- private readonly IClock _clock;
- private readonly IDomainEventAggregator _eventAggregator;
- private readonly IEventBusScope _eventBusScope;
- private bool? _isCompleted;
- private IDisposable _lifetimeLogger;
-
- protected UnitOfWork(IClock clock, ICurrentTHolder identityHolder,
- IDomainEventAggregator eventAggregator, IEventBusScope eventBusScope)
- {
- _clock = clock;
- _eventAggregator = eventAggregator;
- _eventBusScope = eventBusScope;
- IdentityHolder = identityHolder;
- }
-
- public ICurrentTHolder IdentityHolder { get; }
-
- public virtual void Flush()
- {
- Logger.Debug("Flushing unit of work #" + _instanceId);
- UpdateTrackingProperties(IdentityHolder.Current.Name, _clock.UtcNow);
- }
-
- public virtual void Begin()
- {
- _lifetimeLogger = Logger.DebugDuration($"Beginning unit of work #{_instanceId}", $"Disposing unit of work #{_instanceId}");
- _isCompleted = false;
- }
-
- public virtual void Complete()
- {
- Logger.Debug("Completing unit of work #" + _instanceId);
- Flush(); // we have to flush before raising events, therefore the handlers find the latest changes in the DB
- _eventAggregator.RaiseEvents();
- Flush(); // event handlers change the DB state, so we have to flush again
- AsyncHelper.RunSync(()=>_eventBusScope.RaiseEvents());
- _isCompleted = true;
- }
-
- protected abstract void UpdateTrackingProperties(string userId, DateTime utcNow);
-
- protected virtual void Dispose(bool disposing)
- {
- if (disposing)
- {
- if (_isCompleted == false)
- {
- Logger.Info($"Canceling unit of work #{_instanceId}.");
- }
- _lifetimeLogger?.Dispose();
- _lifetimeLogger = null;
- }
- }
-
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
- }
-}
diff --git a/src/abstractions/Backend.Fx/RandomData/Generator.cs b/src/abstractions/Backend.Fx/RandomData/Generator.cs
new file mode 100644
index 00000000..77595ae6
--- /dev/null
+++ b/src/abstractions/Backend.Fx/RandomData/Generator.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Backend.Fx.RandomData
+{
+ public abstract class Generator : IEnumerable
+ {
+ private readonly HashSet _identicalPreventionMemory = new HashSet();
+
+ public IEnumerator GetEnumerator()
+ {
+ const int maxRetries = 1000;
+ int retries = 0;
+ while (true)
+ {
+ T next;
+ int hashCode;
+ do
+ {
+ next = Next();
+ hashCode = next.GetHashCode();
+
+ if (retries++ > maxRetries)
+ {
+ throw new Exception($"Tried {maxRetries} times to generate a unique {typeof(T).Name} but did not succeed, aborting now.");
+ }
+ } while (_identicalPreventionMemory.Contains(hashCode));
+
+ _identicalPreventionMemory.Add(hashCode);
+ yield return next;
+ }
+ // ReSharper disable once IteratorNeverReturns
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ protected abstract T Next();
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/RandomData/LandLineGenerator.cs b/src/abstractions/Backend.Fx/RandomData/LandLineGenerator.cs
new file mode 100644
index 00000000..996f4f02
--- /dev/null
+++ b/src/abstractions/Backend.Fx/RandomData/LandLineGenerator.cs
@@ -0,0 +1,19 @@
+using System.Linq;
+
+namespace Backend.Fx.RandomData
+{
+ public class LandLineGenerator : Generator
+ {
+ public static string Generate()
+ {
+ return new LandLineGenerator().Take(1).Single();
+ }
+
+ protected override string Next()
+ {
+ var generated = Numbers.LandLineNetworks.Random();
+ while (generated.Length < TestRandom.Instance.Next(8, 11)) generated += Numbers.Ciphers.Random();
+ return generated;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/RandomData/Letters.cs b/src/abstractions/Backend.Fx/RandomData/Letters.cs
index 2d49fd04..4d18a3d5 100644
--- a/src/abstractions/Backend.Fx/RandomData/Letters.cs
+++ b/src/abstractions/Backend.Fx/RandomData/Letters.cs
@@ -1,38 +1,29 @@
namespace Backend.Fx.RandomData
{
- public class Letters
+ public static class Letters
{
public static string UpperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static string LowerCase = "abcdefghijklmnopqrstuvwxyz";
public static string RandomUpperCase(int length)
{
- string random = string.Empty;
- for (int i = 0; i < length; i++)
- {
- random += UpperCase.Random();
- }
+ var random = string.Empty;
+ for (var i = 0; i < length; i++) random += UpperCase.Random();
return random;
}
public static string RandomLowerCase(int length)
{
- string random = string.Empty;
- for (int i = 0; i < length; i++)
- {
- random += LowerCase.Random();
- }
+ var random = string.Empty;
+ for (var i = 0; i < length; i++) random += LowerCase.Random();
return random;
}
public static string RandomNormalCase(int length)
{
- string random = string.Empty;
- for (int i = 0; i < length; i++)
- {
- random += i == 0 ? UpperCase.Random() : LowerCase.Random();
- }
+ var random = string.Empty;
+ for (var i = 0; i < length; i++) random += i == 0 ? UpperCase.Random() : LowerCase.Random();
return random;
}
}
-}
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/RandomData/LinqExtensions.cs b/src/abstractions/Backend.Fx/RandomData/LinqExtensions.cs
index 4b6595ae..8fcce937 100644
--- a/src/abstractions/Backend.Fx/RandomData/LinqExtensions.cs
+++ b/src/abstractions/Backend.Fx/RandomData/LinqExtensions.cs
@@ -1,33 +1,30 @@
-namespace Backend.Fx.RandomData
-{
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Dynamic.Core;
- using System.Reflection;
- using BuildingBlocks;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Dynamic.Core;
+using System.Reflection;
+using Backend.Fx.BuildingBlocks;
+namespace Backend.Fx.RandomData
+{
public static class LinqExtensions
{
///
- /// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
+ /// https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
///
///
///
///
public static IEnumerable Shuffle(this IEnumerable source)
{
- if (source == null)
- {
- throw new ArgumentNullException(nameof(source));
- }
+ if (source == null) throw new ArgumentNullException(nameof(source));
- T[] sourceAsArray = source as T[] ?? source.ToArray();
+ var sourceAsArray = source as T[] ?? source.ToArray();
- int n = sourceAsArray.Length;
+ var n = sourceAsArray.Length;
while (n > 1)
{
- int k = TestRandom.Instance.Next(n--);
+ var k = TestRandom.Instance.Next(n--);
T temp = sourceAsArray[n];
sourceAsArray[n] = sourceAsArray[k];
sourceAsArray[k] = temp;
@@ -38,57 +35,41 @@ public static IEnumerable Shuffle(this IEnumerable source)
public static T Random(this IEnumerable source)
{
- if (source == null)
- {
- throw new ArgumentNullException(nameof(source));
- }
+ if (source == null) throw new ArgumentNullException(nameof(source));
// ReSharper disable once PossibleMultipleEnumeration
- if (TryAsQueryable(source, out IQueryable sourceQueryable, out int count))
+ if (TryAsQueryable(source, out var sourceQueryable, out var count))
{
if (count == 0)
- {
throw new ArgumentException(
$"The enumerable of {typeof(T).Name} does not contain any items, therefore no random item can be returned.", nameof(source));
- }
-
+
return sourceQueryable.Skip(TestRandom.Next(count - 1)).First();
}
// ReSharper disable once PossibleMultipleEnumeration
var sourceArray = source as T[] ?? source.ToArray();
if (sourceArray.Length == 0)
- {
throw new ArgumentException(
$"The enumerable of {typeof(T).Name} does not contain any items, therefore no random item can be returned.", nameof(source));
- }
return sourceArray.ElementAt(TestRandom.Next(sourceArray.Length));
}
public static T RandomOrDefault(this IEnumerable source)
{
- if (source == null)
- {
- throw new ArgumentNullException(nameof(source));
- }
+ if (source == null) throw new ArgumentNullException(nameof(source));
// ReSharper disable once PossibleMultipleEnumeration
- if (TryAsQueryable(source, out IQueryable sourceQueryable, out int count))
+ if (TryAsQueryable(source, out var sourceQueryable, out var count))
{
- if (count == 0)
- {
- return default(T);
- }
-
+ if (count == 0) return default;
+
return sourceQueryable.Skip(TestRandom.Next(count - 1)).FirstOrDefault();
}
// ReSharper disable once PossibleMultipleEnumeration
var sourceArray = source as T[] ?? source.ToArray();
- if (sourceArray.Length == 0)
- {
- return default(T);
- }
+ if (sourceArray.Length == 0) return default;
return sourceArray.ElementAt(TestRandom.Next(sourceArray.Length));
}
@@ -105,12 +86,9 @@ private static bool TryAsQueryable(this IEnumerable source, out IQueryable
outQueryable = new T[0].AsQueryable();
return true;
}
-
- var idProperty = typeof(T).GetProperty(nameof(Identified.Id), BindingFlags.Instance | BindingFlags.Public);
- if (idProperty != null)
- {
- sourceQueryable = sourceQueryable.OrderBy(nameof(Identified.Id));
- }
+
+ PropertyInfo idProperty = typeof(T).GetProperty(nameof(Identified.Id), BindingFlags.Instance | BindingFlags.Public);
+ if (idProperty != null) sourceQueryable = sourceQueryable.OrderBy(nameof(Identified.Id));
outQueryable = sourceQueryable;
return true;
diff --git a/src/abstractions/Backend.Fx/RandomData/LoremIpsum.cs b/src/abstractions/Backend.Fx/RandomData/LoremIpsum.cs
index 045ea049..f4546be8 100644
--- a/src/abstractions/Backend.Fx/RandomData/LoremIpsum.cs
+++ b/src/abstractions/Backend.Fx/RandomData/LoremIpsum.cs
@@ -1,31 +1,38 @@
-namespace Backend.Fx.RandomData
+using System.Linq;
+
+namespace Backend.Fx.RandomData
{
- public class LoremIpsum
+ public class LoremIpsumGenerator : Generator
{
- public static string Generate(int minWords, int maxWords, bool asSentence)
+ private static string[] _words = new[]
{
- var words = new[] {
- "lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit", "proin", "eget", "iaculis", "quam", "pellentesque", "elementum", "gravida", "nulla", "at", "tincidunt", "donec", "vulputate", "velit", "sapien", "a", "auctor", "justo", "id", "nunc", "et", "consequat", "magna", "in", "blandit", "ut", "eros", "tempus", "condimentum", "sem", "ac", "feugiat", "tellus", "curabitur", "aliquet", "ultrices", "arcu", "eu", "lacinia", "aliquam", "integer", "non", "venenatis", "sed", "accumsan", "massa", "nibh", "vestibulum", "nec", "porta", "libero", "vel", "ex", "molestie", "pretium", "dignissim", "ligula", "maximus", "placerat", "nisl", "felis", "fringilla", "efficitur", "mi", "nam", "vitae", "orci", "suscipit", "porttitor", "leo", "posuere", "sollicitudin", "dictum", "tristique", "dui", "urna", "quis", "quisque", "semper", "diam", "pulvinar", "erat", "ornare", "maecenas", "euismod", "odio", "tortor", "cursus", "convallis", "enim", "sodales", "facilisis", "faucibus", "fusce", "scelerisque", "purus", "praesent", "interdum", "turpis", "mauris", "duis", "finibus", "augue", "nullam", "mollis", "lacus", "egestas", "metus", "mattis", "morbi", "laoreet", "bibendum", "phasellus", "risus", "neque", "volutpat", "lobortis", "malesuada", "sagittis", "rhoncus", "est", "imperdiet", "aenean", "fermentum", "varius", "vivamus", "suspendisse", "commodo", "luctus", "dapibus", "ullamcorper", "viverra", "congue", "hendrerit", "pharetra", "tempor", "eleifend", "lectus", "te"
- };
-
- var numWords = TestRandom.Instance.Next(maxWords - minWords) + minWords + 1;
+ "lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit", "proin", "eget", "iaculis", "quam", "pellentesque", "elementum", "gravida", "nulla",
+ "at", "tincidunt", "donec", "vulputate", "velit", "sapien", "a", "auctor", "justo", "id", "nunc", "et", "consequat", "magna", "in", "blandit", "ut", "eros",
+ "tempus", "condimentum", "sem", "ac", "feugiat", "tellus", "curabitur", "aliquet", "ultrices", "arcu", "eu", "lacinia", "aliquam", "integer", "non", "venenatis",
+ "sed", "accumsan", "massa", "nibh", "vestibulum", "nec", "porta", "libero", "vel", "ex", "molestie", "pretium", "dignissim", "ligula", "maximus", "placerat",
+ "nisl", "felis", "fringilla", "efficitur", "mi", "nam", "vitae", "orci", "suscipit", "porttitor", "leo", "posuere", "sollicitudin", "dictum", "tristique", "dui",
+ "urna", "quis", "quisque", "semper", "diam", "pulvinar", "erat", "ornare", "maecenas", "euismod", "odio", "tortor", "cursus", "convallis", "enim", "sodales",
+ "facilisis", "faucibus", "fusce", "scelerisque", "purus", "praesent", "interdum", "turpis", "mauris", "duis", "finibus", "augue", "nullam", "mollis", "lacus",
+ "egestas", "metus", "mattis", "morbi", "laoreet", "bibendum", "phasellus", "risus", "neque", "volutpat", "lobortis", "malesuada", "sagittis", "rhoncus", "est",
+ "imperdiet", "aenean", "fermentum", "varius", "vivamus", "suspendisse", "commodo", "luctus", "dapibus", "ullamcorper", "viverra", "congue", "hendrerit", "pharetra",
+ "tempor", "eleifend", "lectus", "te"
+ };
- var result = string.Empty;
-
- for (var w = 0; w < numWords; w++)
- {
- if (w > 0)
- {
- result += " ";
- }
- result += words[TestRandom.Instance.Next(words.Length)];
- }
+ protected override string Next()
+ {
+ return _words[TestRandom.Instance.Next(_words.Length)];
+ }
+ public static string Generate(int minWords, int maxWords, bool asSentence)
+ {
+ int wordCount = TestRandom.Next(minWords, maxWords);
+ string loremIpsumText = string.Join(" ", new LoremIpsumGenerator().Take(wordCount));
if (asSentence)
{
- result += ". ";
+ loremIpsumText += ".";
}
- return result;
- }
+
+ return loremIpsumText;
+ }
}
}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/RandomData/MobileLineGenerator.cs b/src/abstractions/Backend.Fx/RandomData/MobileLineGenerator.cs
new file mode 100644
index 00000000..094e7a25
--- /dev/null
+++ b/src/abstractions/Backend.Fx/RandomData/MobileLineGenerator.cs
@@ -0,0 +1,21 @@
+using System.Linq;
+
+namespace Backend.Fx.RandomData
+{
+ public class MobileLineGenerator : Generator
+ {
+ public static string Generate()
+ {
+ return new MobileLineGenerator().First();
+ }
+
+
+ protected override string Next()
+ {
+ var generated = Numbers.MobileNetworks.Random();
+ while (generated.Length < TestRandom.Instance.Next(11)) generated += Numbers.Ciphers.Random();
+
+ return generated;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/RandomData/Names.cs b/src/abstractions/Backend.Fx/RandomData/Names.cs
index ea9992eb..31e5bdd4 100644
--- a/src/abstractions/Backend.Fx/RandomData/Names.cs
+++ b/src/abstractions/Backend.Fx/RandomData/Names.cs
@@ -2,7 +2,8 @@
{
public static class Names
{
- public static readonly string[] LegalEntityTypes = {
+ public static readonly string[] LegalEntityTypes =
+ {
"GmbH",
"AG",
"KG",
@@ -13,7 +14,8 @@ public static class Names
""
};
- public static readonly string[] CompanyTypes = {
+ public static readonly string[] CompanyTypes =
+ {
"Schuldnerberatung",
"Autohaus",
"Apotheke",
@@ -24,7 +26,8 @@ public static class Names
"Bauunternehmen"
};
- public static readonly string[] Streets = {
+ public static readonly string[] Streets =
+ {
"Abboweg",
"Abteistraße",
"Achatweg",
@@ -3419,7 +3422,8 @@ public static class Names
"Zwitschergasse"
};
- public static readonly string[] Female = {
+ public static readonly string[] Female =
+ {
"Andrea",
"Angelika",
"Anja",
@@ -3492,188 +3496,112 @@ public static class Names
"Yvonne"
};
- public static readonly string[] Family = {
- "Abel",
- "Adelung",
- "Albers",
- "Baars",
- "Baden",
- "Bagwell",
- "Bartels",
- "zum Beck",
- "Becker",
- "Beckmann",
- "Behrmann",
- "Bendel",
- "vom Berge",
- "Berghof",
- "Bergmann",
- "Bier",
- "Blindhammer",
- "Blöte",
- "Boelter",
- "Boers",
- "Bothmann",
- "Brammer",
- "Brandt",
- "Braul",
- "Brelie",
- "Brink",
- "Brockmann",
- "Bruhn",
- "Buckentin",
- "Bünger",
- "Brümmerhoff",
- "Bunke",
- "Bussmann",
- "Böttcher",
- "Clasen",
- "Clausen",
- "Cohrs",
- "Cordes",
- "Dammann",
- "Drewes",
- "Dreyer",
- "Duhne",
- "Ebeling",
- "Eggersglüß",
- "Ehlers",
- "Eickhoff",
- "Eitz",
- "Elling",
- "Engelke",
- "Euhus",
- "Feldmann",
- "Fehlau",
- "Finke",
- "Fintel",
+ public static readonly string[] Family =
+ {
+ "Müller",
+ "Schmidt",
+ "Schneider",
"Fischer",
- "Fornaçon",
- "Freudenthal",
- "Friederichs",
- "Fuhrhop",
- "Gaudineer",
- "Grohnholz",
- "Grotenholte",
- "Grünhagen",
- "Hambrock",
- "Hambruch",
- "Harms",
- "Hartz",
- "Hasselhoff",
- "Hebenbrock",
- "Heldberg",
- "Hellwinkel",
- "Heuer",
- "Hinrichs",
- "Hövermann",
- "Hoops",
- "Hude",
- "Hundt",
- "Husemann",
- "Jacobs",
- "John",
- "Johnson",
- "von der Kammer",
- "Kassebart",
- "Keusemann",
- "Kiss",
- "Klinker",
- "Knupper",
- "Köhnken",
- "Kothe",
- "Kroells",
- "Kronsnest",
- "Küster",
- "Langeloh",
- "Langer",
- "Leitzmann",
- "Loetzke",
- "Lohmann",
- "Lindemann",
- "Lüders",
- "Lüning",
- "Lütjens",
- "Marquard",
- "Meffert",
+ "Weber",
"Meyer",
- "Meyerhoff",
- "Meyermann",
- "Meyers",
- "Müller",
- "Nester",
- "Nolte",
- "Nottorf",
- "Nowka",
- "Oelfke",
- "Ohlau",
- "Oldenburg",
- "Ossowski",
- "Otten",
- "Panning",
- "Piepjunge",
- "Plumhoff",
- "Quast",
- "Ragotzki",
- "Ratje",
- "Reinitz",
- "Reinhardt",
- "Rischmann",
- "Rodewald",
- "Röders",
- "Röhrs",
- "Roloff",
- "Rosebrock",
- "Röttger",
- "von Salzen",
- "Schade",
- "Schepperus",
- "Schloendorf",
- "Schmuck",
+ "Wagner",
+ "Becker",
+ "Schulz",
+ "Hoffmann",
+ "Schäfer",
+ "Koch",
+ "Bauer",
+ "Richter",
+ "Klein",
+ "Wolf",
"Schröder",
- "Schweikert",
- "Sciarra",
- "Siemering",
- "Söhnholz",
- "Springhorn",
- "Steinbach",
- "Stellmann",
- "Stoeckmann",
- "Stolte",
- "Struckhoff",
- "Stünkel",
- "Suhrmüller",
- "Sump",
- "Tanke",
- "Tetzlaff",
- "Thalmann",
- "Tietjen",
- "Tutas",
- "Vollmer",
- "Voß",
- "Wallmann",
- "Weber",
- "Wehrs",
- "Weide",
- "Whitters",
- "Wiebke",
- "Willing",
- "Winkelmann",
- "Wischoff",
- "Wischhoff",
- "Witte",
- "Wittig",
- "Wohlde",
- "Wolkenhauer",
- "Wolters",
- "Wrigge",
- "Wroge",
- "Wüsthoff",
- "Wulfers",
- "Würdemann"
+ "Neumann",
+ "Schwarz",
+ "Zimmermann",
+ "Braun",
+ "Krüger",
+ "Hofmann",
+ "Hartmann",
+ "Lange",
+ "Schmitt",
+ "Werner",
+ "Schmitz",
+ "Krause",
+ "Meier",
+ "Lehmann",
+ "Schmid",
+ "Schulze",
+ "Maier",
+ "Köhler",
+ "Herrmann",
+ "König",
+ "Walter",
+ "Mayer",
+ "Huber",
+ "Kaiser",
+ "Fuchs",
+ "Peters",
+ "Lang",
+ "Scholz",
+ "Möller",
+ "Weiß",
+ "Jung",
+ "Hahn",
+ "Schubert",
+ "Vogel",
+ "Friedrich",
+ "Keller",
+ "Günther",
+ "Frank",
+ "Berger",
+ "Winkler",
+ "Roth",
+ "Beck",
+ "Lorenz",
+ "Baumann",
+ "Franke",
+ "Albrecht",
+ "Schuster",
+ "Simon",
+ "Ludwig",
+ "Böhm",
+ "Winter",
+ "Kraus",
+ "Martin",
+ "Schumacher",
+ "Krämer",
+ "Vogt",
+ "Stein",
+ "Jäger",
+ "Otto",
+ "Sommer",
+ "Groß",
+ "Seidel",
+ "Heinrich",
+ "Brandt",
+ "Haas",
+ "Schreiber",
+ "Graf",
+ "Schulte",
+ "Dietrich",
+ "Ziegler",
+ "Kuhn",
+ "Kühn",
+ "Pohl",
+ "Engel",
+ "Horn",
+ "Busch",
+ "Bergmann",
+ "Thomas",
+ "Voigt",
+ "Sauer",
+ "Arnold",
+ "Wolff",
+ "Pfeiffer"
};
- public static readonly string[] Male = {
+ public static readonly string[] Male =
+ {
"Alexander",
"Andreas",
"Benjamin",
@@ -3741,7 +3669,8 @@ public static class Names
"Wolfgang"
};
- public static readonly string[] Departments = {
+ public static readonly string[] Departments =
+ {
"Vertrieb",
"Einkauf",
"IT",
@@ -3749,7 +3678,8 @@ public static class Names
"Geschäftsleitung"
};
- public static readonly string[] Cities = {
+ public static readonly string[] Cities =
+ {
"Aach (Hegau)",
"Aach",
"Aachen",
@@ -5818,10 +5748,11 @@ public static class Names
"Zwönitz"
};
- public static readonly string[] Countries = {
+ public static readonly string[] Countries =
+ {
"Austria",
"Germany",
- "Switzerland",
+ "Switzerland"
};
}
}
\ No newline at end of file
diff --git a/src/abstractions/Backend.Fx/RandomData/Numbers.cs b/src/abstractions/Backend.Fx/RandomData/Numbers.cs
index dfeacd49..182cd7c8 100644
--- a/src/abstractions/Backend.Fx/RandomData/Numbers.cs
+++ b/src/abstractions/Backend.Fx/RandomData/Numbers.cs
@@ -1,14 +1,15 @@
-namespace Backend.Fx.RandomData
-{
- using System.Globalization;
- using System.Linq;
+using System.Globalization;
+using System.Linq;
- public class Numbers
+namespace Backend.Fx.RandomData
+{
+ public static class Numbers
{
public static readonly string[] Ciphers = Enumerable.Range(0, 10).Select(i => i.ToString()).ToArray();
public static readonly string[] PostalCodes = Enumerable.Range(9000, 90999).Select(i => i.ToString("00000")).ToArray();
- public static readonly string[] LandLineNetworks = {
+ public static readonly string[] LandLineNetworks =
+ {
"0201",
"0202",
"0203",
@@ -5220,7 +5221,9 @@ public class Numbers
"09977",
"09978"
};
- public static readonly string[] MobileNetworks = {
+
+ public static readonly string[] MobileNetworks =
+ {
"01510",
"01511",
"01512",
@@ -5285,14 +5288,9 @@ public static string RandomHouseNumber()
{
var next = TestRandom.Next(100);
var nr = TestRandom.Next(100);
- if (next < 10)
- {
- return $"{nr} - {nr + TestRandom.Next(1, 5)}";
- }
- if (next < 30)
- {
- return nr.ToString(CultureInfo.InvariantCulture) + "abcd".Random();
- }
+ if (next < 10) return $"{nr} - {nr + TestRandom.Next(1, 5)}";
+
+ if (next < 30) return nr.ToString(CultureInfo.InvariantCulture) + "abcd".Random();
return nr.ToString(CultureInfo.InvariantCulture);
}
diff --git a/src/abstractions/Backend.Fx/RandomData/TestAddress.cs b/src/abstractions/Backend.Fx/RandomData/TestAddress.cs
index 46c7f672..735e3f9e 100644
--- a/src/abstractions/Backend.Fx/RandomData/TestAddress.cs
+++ b/src/abstractions/Backend.Fx/RandomData/TestAddress.cs
@@ -1,6 +1,9 @@
-namespace Backend.Fx.RandomData
+using System.Collections.Generic;
+using Backend.Fx.BuildingBlocks;
+
+namespace Backend.Fx.RandomData
{
- public class TestAddress
+ public class TestAddress : ValueObject
{
public TestAddress(string street, string number, string postalCode, string city, string country)
{
@@ -20,5 +23,14 @@ public TestAddress(string street, string number, string postalCode, string city,
public string City { get; }
public string Country { get; }
+
+ protected override IEnumerable
- public abstract class DomainModule : SimpleInjectorModule
+ public class SimpleInjectorDomainModule : SimpleInjectorModule
{
- private static readonly ILogger Logger = LogManager.Create();
+ private static readonly ILogger Logger = LogManager.Create();
private readonly Assembly[] _domainAssemblies;
private readonly string _domainAssembliesForLogging;
- protected DomainModule(params Assembly[] domainAssemblies)
+ public SimpleInjectorDomainModule(params Assembly[] domainAssemblies)
{
_domainAssemblies = domainAssemblies;
_domainAssembliesForLogging = string.Join(",", _domainAssemblies.Select(ass => ass.GetName().Name));
@@ -37,25 +39,25 @@ protected override void Register(Container container, ScopedLifestyle scopedLife
RegisterAuthorization(container);
// all jobs are dynamically registered
- foreach (var jobType in container.GetTypesToRegister(typeof(IJob), _domainAssemblies))
+ foreach (Type jobType in container.GetTypesToRegister(typeof(IJob), _domainAssemblies))
{
Logger.Debug($"Registering {jobType.Name}");
container.Register(jobType);
}
- // initial data generation subsystem
- foreach (var dataGeneratorType in container.GetTypesToRegister(typeof(IDataGenerator), _domainAssemblies))
- {
- Logger.Debug($"Appending {dataGeneratorType.Name} to list of IDataGenerators");
- container.Collection.Append(typeof(IDataGenerator), dataGeneratorType);
- }
-
// domain event handlers
- foreach (var domainEventHandlerType in container.GetTypesToRegister(typeof(IDomainEventHandler<>), _domainAssemblies))
+ foreach (Type domainEventHandlerType in container.GetTypesToRegister(typeof(IDomainEventHandler<>), _domainAssemblies))
{
Logger.Debug($"Appending {domainEventHandlerType.Name} to list of IDomainEventHandler");
container.Collection.Append(typeof(IDomainEventHandler<>), domainEventHandlerType);
}
+
+ // integration message handlers
+ foreach (Type integrationMessageHandlerType in container.GetTypesToRegister(typeof(IIntegrationMessageHandler<>), _domainAssemblies))
+ {
+ Logger.Debug($"Registering {integrationMessageHandlerType.Name}");
+ container.Register(integrationMessageHandlerType);
+ }
}
private void RegisterDomainAndApplicationServices(Container container)
@@ -91,7 +93,7 @@ private void RegisterAuthorization(Container container)
{
Logger.Debug($"Registering authorization services from {_domainAssembliesForLogging}");
var aggregateRootAuthorizationTypes = container.GetTypesToRegister(typeof(IAggregateAuthorization<>), _domainAssemblies).ToArray();
- foreach (var aggregateAuthorizationType in aggregateRootAuthorizationTypes)
+ foreach (Type aggregateAuthorizationType in aggregateRootAuthorizationTypes)
{
var serviceTypes = aggregateAuthorizationType
.GetTypeInfo()
@@ -100,7 +102,7 @@ private void RegisterAuthorization(Container container)
&& impif.GenericTypeArguments.Length == 1
&& typeof(AggregateRoot).GetTypeInfo().IsAssignableFrom(impif.GenericTypeArguments[0].GetTypeInfo()));
- foreach (var serviceType in serviceTypes)
+ foreach (Type serviceType in serviceTypes)
{
Logger.Debug($"Registering scoped authorization service {serviceType.Name} with implementation {aggregateAuthorizationType.Name}");
container.Register(serviceType, aggregateAuthorizationType);
diff --git a/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/Modules/SimpleInjectorInfrastructureModule.cs b/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/Modules/SimpleInjectorInfrastructureModule.cs
new file mode 100644
index 00000000..e9331a84
--- /dev/null
+++ b/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/Modules/SimpleInjectorInfrastructureModule.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Reflection;
+using Backend.Fx.Patterns.DependencyInjection;
+using SimpleInjector;
+
+namespace Backend.Fx.SimpleInjectorDependencyInjection.Modules
+{
+ public class SimpleInjectorInfrastructureModule : IInfrastructureModule
+ {
+ private readonly Container _container;
+
+ public SimpleInjectorInfrastructureModule(Container container)
+ {
+ _container = container;
+ }
+
+ public void RegisterScoped() where TService : class where TImpl : class, TService
+ {
+ _container.Register();
+ }
+
+ public void RegisterScoped(Func factory) where TService : class
+ {
+ _container.Register(factory);
+ }
+
+ public void RegisterScoped(Type serviceType, Assembly[] assembliesToScan)
+ {
+ _container.Register(serviceType, assembliesToScan);
+ }
+
+ public void RegisterDecorator() where TService : class where TImpl : class, TService
+ {
+ _container.RegisterDecorator();
+ }
+
+ public void RegisterScoped(Type serviceType, Type implementationType)
+ {
+ _container.Register(serviceType, implementationType);
+ }
+
+ public void RegisterSingleton() where TService : class where TImpl : class, TService
+ {
+ _container.RegisterSingleton();
+ }
+
+ public void RegisterInstance(TService instance) where TService : class
+ {
+ _container.RegisterInstance(instance);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/Modules/SimpleInjectorModule.cs b/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/Modules/SimpleInjectorModule.cs
index 0c3fbd28..cd8abf4d 100644
--- a/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/Modules/SimpleInjectorModule.cs
+++ b/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/Modules/SimpleInjectorModule.cs
@@ -13,7 +13,7 @@ public abstract class SimpleInjectorModule : IModule
public virtual void Register(ICompositionRoot compositionRoot)
{
Logger.Debug($"Registering {GetType().Name}");
- SimpleInjectorCompositionRoot simpleInjectorCompositionRoot = (SimpleInjectorCompositionRoot) compositionRoot;
+ var simpleInjectorCompositionRoot = (SimpleInjectorCompositionRoot) compositionRoot;
Register(simpleInjectorCompositionRoot.Container, simpleInjectorCompositionRoot.ScopedLifestyle);
}
}
diff --git a/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/SimpleInjectorCompositionRoot.cs b/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/SimpleInjectorCompositionRoot.cs
index f20dcf81..18086308 100644
--- a/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/SimpleInjectorCompositionRoot.cs
+++ b/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/SimpleInjectorCompositionRoot.cs
@@ -1,9 +1,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Threading;
using Backend.Fx.Logging;
using Backend.Fx.Patterns.DependencyInjection;
using Backend.Fx.Patterns.EventAggregation.Domain;
+using Backend.Fx.SimpleInjectorDependencyInjection.Modules;
using SimpleInjector;
using SimpleInjector.Advanced;
using SimpleInjector.Lifestyles;
@@ -17,6 +19,7 @@ public class SimpleInjectorCompositionRoot : ICompositionRoot
{
private static readonly ILogger Logger = LogManager.Create();
+ private int _scopeSequenceNumber = 1;
///
/// This constructor creates a composition root that prefers scoped lifestyle
///
@@ -30,7 +33,8 @@ public SimpleInjectorCompositionRoot(ILifestyleSelectionBehavior lifestyleBehavi
ScopedLifestyle = scopedLifestyle;
Container.Options.LifestyleSelectionBehavior = lifestyleBehavior;
Container.Options.DefaultScopedLifestyle = ScopedLifestyle;
- Container.Register, CurrentCorrelationHolder>();
+ InfrastructureModule = new SimpleInjectorInfrastructureModule(Container);
+ InstanceProvider = new SimpleInjectorInstanceProvider(Container);
}
public Container Container { get; } = new Container();
@@ -84,24 +88,14 @@ public IEnumerable GetInstances() where T : class
}
///
- public IDisposable BeginScope()
+ public IInjectionScope BeginScope()
{
- var scope = AsyncScopedLifestyle.BeginScope(Container);
- return scope;
+ return new SimpleInjectorInjectionScope(Interlocked.Increment(ref _scopeSequenceNumber), AsyncScopedLifestyle.BeginScope(Container));
}
- public bool TryGetCurrentCorrelation(out Correlation correlation)
- {
- Scope scope = ScopedLifestyle.GetCurrentScope(Container);
- if (scope == null)
- {
- correlation = null;
- return false;
- }
+ public IInstanceProvider InstanceProvider { get; }
- correlation = scope.GetInstance>().Current;
- return true;
- }
+ public IInfrastructureModule InfrastructureModule { get; }
public Scope GetCurrentScope()
{
diff --git a/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/SimpleInjectorInjectionScope.cs b/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/SimpleInjectorInjectionScope.cs
new file mode 100644
index 00000000..c22eedc3
--- /dev/null
+++ b/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/SimpleInjectorInjectionScope.cs
@@ -0,0 +1,24 @@
+using Backend.Fx.Patterns.DependencyInjection;
+using SimpleInjector;
+
+namespace Backend.Fx.SimpleInjectorDependencyInjection
+{
+ public class SimpleInjectorInjectionScope : InjectionScope
+ {
+ private readonly Scope _scope;
+
+ public SimpleInjectorInjectionScope(int sequenceNumber, Scope scope) : base(sequenceNumber)
+ {
+ _scope = scope;
+ InstanceProvider = new SimpleInjectorInstanceProvider(_scope.Container);
+ }
+
+ public override IInstanceProvider InstanceProvider { get; }
+
+
+ public override void Dispose()
+ {
+ _scope.Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/SimpleInjectorInstanceProvider.cs b/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/SimpleInjectorInstanceProvider.cs
new file mode 100644
index 00000000..610dd71b
--- /dev/null
+++ b/src/implementations/Backend.Fx.SimpleInjetorDependencyInjection/SimpleInjectorInstanceProvider.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Backend.Fx.Patterns.DependencyInjection;
+using SimpleInjector;
+
+namespace Backend.Fx.SimpleInjectorDependencyInjection
+{
+ public class SimpleInjectorInstanceProvider : IInstanceProvider
+ {
+ private readonly Container _container;
+
+ public SimpleInjectorInstanceProvider(Container container)
+ {
+ _container = container;
+ }
+
+ public object GetInstance(Type serviceType)
+ {
+ return _container.GetInstance(serviceType);
+ }
+
+ public IEnumerable GetInstances(Type serviceType)
+ {
+ return (IEnumerable) _container.GetInstance(typeof(IEnumerable<>).MakeGenericType(serviceType));
+ }
+
+ public T GetInstance() where T : class
+ {
+ return _container.GetInstance();
+ }
+
+ public IEnumerable GetInstances() where T : class
+ {
+ return _container.GetInstance>();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/Backend.Fx.EfCorePersistence.Tests.csproj b/tests/Backend.Fx.EfCorePersistence.Tests/Backend.Fx.EfCorePersistence.Tests.csproj
index 492df4cc..6bdd9f62 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/Backend.Fx.EfCorePersistence.Tests.csproj
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/Backend.Fx.EfCorePersistence.Tests.csproj
@@ -5,20 +5,23 @@
-
+
-
-
-
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
-
+
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/DbConnectionEx.cs b/tests/Backend.Fx.EfCorePersistence.Tests/DbConnectionEx.cs
index c4c1d899..499d8a51 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/DbConnectionEx.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/DbConnectionEx.cs
@@ -9,7 +9,7 @@ public static class DbConnectionEx
{
public static void ExecuteNonQuery(this IDbConnection openConnection, string cmd)
{
- using (var command = openConnection.CreateCommand())
+ using (IDbCommand command = openConnection.CreateCommand())
{
command.CommandText = cmd;
command.ExecuteNonQuery();
@@ -18,29 +18,23 @@ public static void ExecuteNonQuery(this IDbConnection openConnection, string cmd
public static T ExecuteScalar(this IDbConnection openConnection, string cmd)
{
- using (var command = openConnection.CreateCommand())
+ using (IDbCommand command = openConnection.CreateCommand())
{
command.CommandText = cmd;
object scalarResult = command.ExecuteScalar();
- if (typeof(T) == typeof(int))
- {
- return (T)(object)Convert.ToInt32(scalarResult);
- }
- return (T)scalarResult;
+ if (typeof(T) == typeof(int)) return (T) (object) Convert.ToInt32(scalarResult);
+ return (T) scalarResult;
}
}
[UsedImplicitly]
public static IEnumerable ExecuteReader(this IDbConnection openConnection, string cmd, Func forEachResultFunc)
{
- using (var command = openConnection.CreateCommand())
+ using (IDbCommand command = openConnection.CreateCommand())
{
command.CommandText = cmd;
IDataReader reader = command.ExecuteReader();
- while (reader.NextResult())
- {
- yield return forEachResultFunc(reader);
- }
+ while (reader.NextResult()) yield return forEachResultFunc(reader);
}
}
}
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/DbSession.cs b/tests/Backend.Fx.EfCorePersistence.Tests/DbSession.cs
deleted file mode 100644
index 8a466999..00000000
--- a/tests/Backend.Fx.EfCorePersistence.Tests/DbSession.cs
+++ /dev/null
@@ -1,107 +0,0 @@
-using System;
-using System.Data.Common;
-using System.Security.Principal;
-using Backend.Fx.EfCorePersistence.Tests.DummyImpl.Persistence;
-using Backend.Fx.Environment.Authentication;
-using Backend.Fx.Environment.DateAndTime;
-using Backend.Fx.Patterns.DependencyInjection;
-using Backend.Fx.Patterns.EventAggregation.Domain;
-using Backend.Fx.Patterns.EventAggregation.Integration;
-using Backend.Fx.Patterns.Transactions;
-using Backend.Fx.Patterns.UnitOfWork;
-using FakeItEasy;
-using Microsoft.EntityFrameworkCore;
-
-namespace Backend.Fx.EfCorePersistence.Tests
-{
- public class DbSession : IDisposable
- {
- public DbSession(DbConnection connection, DbContextOptionsBuilder optionsBuilder)
- {
- Connection = connection;
- OptionsBuilder = optionsBuilder;
- connection.Open();
- }
-
- public DbConnection Connection { get; }
- public DbContextOptionsBuilder OptionsBuilder { get; }
-
- public IUnitOfWork BeginUnitOfWork(bool asReadonly = false, IClock clock = null, IIdentity identity = null)
- {
- ICurrentTHolder currentIdentityHolder = new CurrentIdentityHolder();
- currentIdentityHolder.ReplaceCurrent(identity ?? new SystemIdentity());
-
- var dbContext = new TestDbContext(OptionsBuilder.Options);
- IUnitOfWork unitOfWork = new EfUnitOfWork(clock ?? new FrozenClock(),
- currentIdentityHolder,
- A.Fake(),
- A.Fake(),
- dbContext);
-
- ITransactionContext transactionContext = new TransactionContext(Connection);
- if (asReadonly)
- {
- transactionContext = new ReadonlyDecorator(transactionContext);
- }
-
- unitOfWork = new DbContextTransactionDecorator(transactionContext,dbContext, unitOfWork);
- // unitOfWork = new DbConnectionDecorator(Connection, unitOfWork);
-
- unitOfWork.Begin();
- return unitOfWork;
- }
-
- public void Dispose()
- {
- Connection?.Close();
- }
- }
-
- public static class TestEx
- {
- public static DbContext GetDbContext(this IUnitOfWork unitOfWork)
- {
- if (unitOfWork is EfUnitOfWork efUnitOfWork)
- {
- return efUnitOfWork.DbContext;
- }
-
- if (unitOfWork is DbContextTransactionDecorator dbcTransactionDecorator)
- {
- return GetDbContext(dbcTransactionDecorator.UnitOfWork);
- }
-
- if (unitOfWork is DbTransactionDecorator transactionDecorator)
- {
- return GetDbContext(transactionDecorator.UnitOfWork);
- }
-
- if (unitOfWork is DbConnectionDecorator connectionDecorator)
- {
- return GetDbContext(connectionDecorator.UnitOfWork);
- }
-
- throw new InvalidOperationException();
- }
-
- public static DbTransaction GetDbTransaction(this IUnitOfWork unitOfWork)
- {
- if (unitOfWork is DbContextTransactionDecorator dbcTransactionDecorator)
- {
- return (DbTransaction) dbcTransactionDecorator.TransactionContext.CurrentTransaction;
- }
-
- if (unitOfWork is DbTransactionDecorator transactionDecorator)
- {
- return (DbTransaction) transactionDecorator.TransactionContext.CurrentTransaction;
- }
-
- if (unitOfWork is DbConnectionDecorator connectionDecorator)
- {
- return GetDbTransaction(connectionDecorator.UnitOfWork);
- }
-
- throw new InvalidOperationException();
- }
- }
-}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/Blog.cs b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/Blog.cs
index 5cb852e7..27c8be41 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/Blog.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/Blog.cs
@@ -8,7 +8,9 @@ namespace Backend.Fx.EfCorePersistence.Tests.DummyImpl.Domain
public class Blog : AggregateRoot
{
[UsedImplicitly]
- private Blog() { }
+ private Blog()
+ {
+ }
public Blog(int id, string name) : base(id)
{
@@ -19,7 +21,7 @@ public Blog(int id, string name) : base(id)
public ISet Posts { get; } = new HashSet();
- public Post AddPost(IEntityIdGenerator idGenerator, string name, bool isPublic=false)
+ public Post AddPost(IEntityIdGenerator idGenerator, string name, bool isPublic = false)
{
var post = new Post(idGenerator.NextId(), this, name, isPublic);
Posts.Add(post);
@@ -29,10 +31,7 @@ public Post AddPost(IEntityIdGenerator idGenerator, string name, bool isPublic=f
public void Modify(string modified)
{
Name = modified;
- foreach (var post in Posts)
- {
- post.SetName(modified);
- }
+ foreach (Post post in Posts) post.SetName(modified);
}
}
}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/BlogAuthorization.cs b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/BlogAuthorization.cs
index f89f183a..958e27a8 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/BlogAuthorization.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/BlogAuthorization.cs
@@ -5,5 +5,6 @@ namespace Backend.Fx.EfCorePersistence.Tests.DummyImpl.Domain
{
[UsedImplicitly]
public class BlogAuthorization : AllowAll
- { }
-}
+ {
+ }
+}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/Blogger.cs b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/Blogger.cs
index 86ace3c3..a21db5ab 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/Blogger.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/Blogger.cs
@@ -7,7 +7,8 @@ public class Blogger : AggregateRoot
{
[UsedImplicitly]
private Blogger()
- { }
+ {
+ }
public Blogger(int id, string lastName, string firstName) : base(id)
{
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/Post.cs b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/Post.cs
index 4e5c869e..e71875c0 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/Post.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Domain/Post.cs
@@ -7,9 +7,11 @@ namespace Backend.Fx.EfCorePersistence.Tests.DummyImpl.Domain
public class Post : Entity
{
[UsedImplicitly]
- private Post() { }
+ private Post()
+ {
+ }
- public Post(int id, Blog blog, string name, bool isPublic=false) : base(id)
+ public Post(int id, Blog blog, string name, bool isPublic = false) : base(id)
{
Blog = blog;
BlogId = blog.Id;
@@ -17,14 +19,13 @@ public Post(int id, Blog blog, string name, bool isPublic=false) : base(id)
TargetAudience = new TargetAudience {IsPublic = isPublic, Culture = "fr-FR"};
}
- [UsedImplicitly]
- public int BlogId { get; private set; }
- [UsedImplicitly]
- public Blog Blog { get; private set; }
- [UsedImplicitly]
- public string Name { get; private set; }
- [UsedImplicitly]
- public TargetAudience TargetAudience { get; set; }
+ [UsedImplicitly] public int BlogId { get; private set; }
+
+ [UsedImplicitly] public Blog Blog { get; private set; }
+
+ [UsedImplicitly] public string Name { get; private set; }
+
+ [UsedImplicitly] public TargetAudience TargetAudience { get; set; }
public void SetName(string name)
{
@@ -37,6 +38,7 @@ public class TargetAudience : ValueObject
public string Culture { get; set; }
public bool IsPublic { get; set; }
+
protected override IEnumerable GetEqualityComponents()
{
yield return Culture;
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/BlogMapping.cs b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/BlogMapping.cs
index 4d5b8411..4f21dddb 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/BlogMapping.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/BlogMapping.cs
@@ -8,21 +8,20 @@ namespace Backend.Fx.EfCorePersistence.Tests.DummyImpl.Persistence
{
public class BlogMapping : AggregateMapping
{
- public override void ApplyEfMapping(ModelBuilder modelBuilder)
- {
- modelBuilder.Entity().OwnsOne(p => p.TargetAudience);
- }
-
public override IEnumerable>> IncludeDefinitions
{
get
{
return new Expression>[]
{
- blog => blog.Posts,
+ blog => blog.Posts
};
}
}
+ public override void ApplyEfMapping(ModelBuilder modelBuilder)
+ {
+ modelBuilder.Entity().OwnsOne(p => p.TargetAudience);
+ }
}
}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/BloggerMapping.cs b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/BloggerMapping.cs
index d8afb740..2659bcd9 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/BloggerMapping.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/BloggerMapping.cs
@@ -3,5 +3,6 @@
namespace Backend.Fx.EfCorePersistence.Tests.DummyImpl.Persistence
{
public class BloggerMapping : PlainAggregateMapping
- { }
+ {
+ }
}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/TestDbContext.cs b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/TestDbContext.cs
index fea17c48..bd40f2f5 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/TestDbContext.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/TestDbContext.cs
@@ -12,17 +12,17 @@ public TestDbContext([NotNull] DbContextOptions options) : base(o
Database.AutoTransactionsEnabled = false;
}
+ public DbSet Bloggers { get; [UsedImplicitly] set; }
+
+ public DbSet Blogs { get; [UsedImplicitly] set; }
+ public DbSet Posts { get; [UsedImplicitly] set; }
+ public DbSet Tenants { get; [UsedImplicitly] set; }
+
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
this.ApplyAggregateMappings(modelBuilder);
modelBuilder.RegisterRowVersionProperty();
modelBuilder.RegisterEntityIdAsNeverGenerated();
}
-
- public DbSet Bloggers { get; [UsedImplicitly] set; }
-
- public DbSet Blogs { get; [UsedImplicitly] set; }
- public DbSet Posts { get; [UsedImplicitly] set; }
- public DbSet Tenants { get; [UsedImplicitly] set; }
}
-}
+}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/TestDbContextFactory.cs b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/TestDbContextFactory.cs
index 85280300..cefd3713 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/TestDbContextFactory.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/DummyImpl/Persistence/TestDbContextFactory.cs
@@ -5,7 +5,8 @@
namespace Backend.Fx.EfCorePersistence.Tests.DummyImpl.Persistence
{
- [UsedImplicitly, Obsolete("Only for migration support at design time")]
+ [UsedImplicitly]
+ [Obsolete("Only for migration support at design time")]
public class TestDbContextFactory : IDesignTimeDbContextFactory
{
public TestDbContext CreateDbContext(string[] args)
@@ -13,4 +14,4 @@ public TestDbContext CreateDbContext(string[] args)
return new TestDbContext(new DbContextOptionsBuilder().UseSqlite("DataSource=:memory:").Options);
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/DatabaseFixture.cs b/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/DatabaseFixture.cs
index 4a8c820e..59578f8c 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/DatabaseFixture.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/DatabaseFixture.cs
@@ -1,10 +1,14 @@
-using System.Data.Common;
+using System.Data;
+using System.Security.Principal;
using Backend.Fx.EfCorePersistence.Tests.DummyImpl.Persistence;
+using Backend.Fx.Environment.Authentication;
+using Backend.Fx.Environment.DateAndTime;
+using Backend.Fx.Environment.Persistence;
using Microsoft.EntityFrameworkCore;
namespace Backend.Fx.EfCorePersistence.Tests.Fixtures
{
- public abstract class DatabaseFixture
+ public abstract class DatabaseFixture
{
public void CreateDatabase()
{
@@ -16,8 +20,28 @@ public void CreateDatabase()
protected abstract DbContextOptions GetDbContextOptionsForDbCreation();
- protected abstract DbContextOptionsBuilder GetDbContextOptionsBuilder(DbConnection connection);
+ public abstract DbContextOptionsBuilder GetDbContextOptionsBuilder(IDbConnection connection);
- public abstract DbSession UseDbSession();
+ public abstract DbConnectionOperationDecorator UseOperation();
+
+ public TestDbSession CreateTestDbSession(DbConnectionOperationDecorator operation = null, IIdentity asIdentity = null, IClock clock = null)
+ {
+ CurrentIdentityHolder CreateAsIdentity()
+ {
+ var cih = new CurrentIdentityHolder();
+ cih.ReplaceCurrent(asIdentity);
+ return cih;
+ }
+
+ clock ??= new WallClock();
+ operation ??= UseOperation();
+
+ operation.Begin();
+
+ var identityHolder = asIdentity == null
+ ? CurrentIdentityHolder.CreateSystem()
+ : CreateAsIdentity();
+ return new TestDbSession(this, operation, identityHolder, clock);
+ }
}
}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/SqlServerDatabaseFixture.cs b/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/SqlServerDatabaseFixture.cs
index 14da31b6..5f57d5b2 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/SqlServerDatabaseFixture.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/SqlServerDatabaseFixture.cs
@@ -1,8 +1,9 @@
using System;
using System.Data;
-using System.Data.Common;
using System.Data.SqlClient;
using Backend.Fx.EfCorePersistence.Tests.DummyImpl.Persistence;
+using Backend.Fx.Environment.Persistence;
+using Backend.Fx.Patterns.DependencyInjection;
using Microsoft.EntityFrameworkCore;
namespace Backend.Fx.EfCorePersistence.Tests.Fixtures
@@ -15,29 +16,30 @@ public class SqlServerDatabaseFixture : DatabaseFixture
public SqlServerDatabaseFixture()
{
- string dbName = $"TestFixture_{_testindex++:000}";
+ var dbName = $"TestFixture_{_testindex++:000}";
var sqlConnectionStringBuilder = new SqlConnectionStringBuilder("Server=.\\SQLExpress;Trusted_Connection=True;");
using (IDbConnection connection = new SqlConnection(sqlConnectionStringBuilder.ConnectionString))
{
connection.Open();
- using (var dropCommand = connection.CreateCommand())
+ using (IDbCommand dropCommand = connection.CreateCommand())
{
dropCommand.CommandText = $"IF EXISTS(SELECT * FROM sys.Databases WHERE Name='{dbName}') ALTER DATABASE [{dbName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE";
dropCommand.ExecuteNonQuery();
}
- using (var dropCommand = connection.CreateCommand())
+ using (IDbCommand dropCommand = connection.CreateCommand())
{
dropCommand.CommandText = $"IF EXISTS(SELECT * FROM sys.Databases WHERE Name='{dbName}') DROP DATABASE [{dbName}]";
dropCommand.ExecuteNonQuery();
}
- using (var createCommand = connection.CreateCommand())
+ using (IDbCommand createCommand = connection.CreateCommand())
{
createCommand.CommandText = $"CREATE DATABASE [{dbName}]";
createCommand.ExecuteNonQuery();
}
+
connection.Close();
}
@@ -50,16 +52,18 @@ protected override DbContextOptions GetDbContextOptionsForDbCreat
return new DbContextOptionsBuilder().UseSqlServer(_connectionString).Options;
}
-
- protected override DbContextOptionsBuilder GetDbContextOptionsBuilder(DbConnection connection)
+
+ public override DbContextOptionsBuilder GetDbContextOptionsBuilder(IDbConnection connection)
{
- return new DbContextOptionsBuilder().UseSqlServer(connection);
+ return new DbContextOptionsBuilder().UseSqlServer((SqlConnection) connection);
}
- public override DbSession UseDbSession()
+ public override DbConnectionOperationDecorator UseOperation()
{
- var sqlConnection = new SqlConnection(_connectionString);
- return new DbSession(sqlConnection, GetDbContextOptionsBuilder(sqlConnection));
+ var sqliteConnection = new SqlConnection(_connectionString);
+ IOperation operation = new Operation();
+ operation = new DbTransactionOperationDecorator(sqliteConnection, operation);
+ return new DbConnectionOperationDecorator(sqliteConnection, operation);
}
}
}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/SqliteDatabaseFixture.cs b/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/SqliteDatabaseFixture.cs
index 0ba762ea..1fa813b7 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/SqliteDatabaseFixture.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/SqliteDatabaseFixture.cs
@@ -1,6 +1,9 @@
+using System.Data;
using System.Data.Common;
using System.IO;
using Backend.Fx.EfCorePersistence.Tests.DummyImpl.Persistence;
+using Backend.Fx.Environment.Persistence;
+using Backend.Fx.Patterns.DependencyInjection;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
@@ -15,15 +18,17 @@ protected override DbContextOptions GetDbContextOptionsForDbCreat
return new DbContextOptionsBuilder().UseSqlite(_connectionString).Options;
}
- protected override DbContextOptionsBuilder GetDbContextOptionsBuilder(DbConnection connection)
+ public override DbContextOptionsBuilder GetDbContextOptionsBuilder(IDbConnection connection)
{
- return new DbContextOptionsBuilder().UseSqlite(connection);
+ return new DbContextOptionsBuilder().UseSqlite((SqliteConnection) connection);
}
- public override DbSession UseDbSession()
+ public override DbConnectionOperationDecorator UseOperation()
{
var sqliteConnection = new SqliteConnection(_connectionString);
- return new DbSession(sqliteConnection, GetDbContextOptionsBuilder(sqliteConnection));
+ IOperation operation = new Operation();
+ operation = new DbTransactionOperationDecorator(sqliteConnection, operation);
+ return new DbConnectionOperationDecorator(sqliteConnection, operation);
}
}
}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/TestDbSession.cs b/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/TestDbSession.cs
new file mode 100644
index 00000000..8fe47f7f
--- /dev/null
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/Fixtures/TestDbSession.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Data;
+using System.Security.Principal;
+using Backend.Fx.EfCorePersistence.Tests.DummyImpl.Persistence;
+using Backend.Fx.Environment.DateAndTime;
+using Backend.Fx.Environment.Persistence;
+using Backend.Fx.Patterns.DependencyInjection;
+
+namespace Backend.Fx.EfCorePersistence.Tests.Fixtures
+{
+ public class TestDbSession : ICanFlush, IDisposable
+ {
+ private readonly DbConnectionOperationDecorator _operation;
+ private readonly EfFlush _efFlush;
+
+ public TestDbSession(DatabaseFixture fixture, DbConnectionOperationDecorator operation, ICurrentTHolder identityHolder, IClock clock)
+ {
+ _operation = operation;
+ DbContext = new TestDbContext(fixture.GetDbContextOptionsBuilder(operation.DbConnection).Options);
+ _efFlush = new EfFlush(DbContext, identityHolder, clock);
+ DbConnection = operation.DbConnection;
+ }
+
+
+ public TestDbContext DbContext { get; }
+ public IDbConnection DbConnection { get; }
+
+ public void Flush()
+ {
+ _efFlush.Flush();
+ }
+
+ public void Dispose()
+ {
+ _efFlush.Flush();
+ DbContext.Dispose();
+ _operation.Complete();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/Migrations/20190624150947_Initial.cs b/tests/Backend.Fx.EfCorePersistence.Tests/Migrations/20190624150947_Initial.cs
index 53fad055..841db0cf 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/Migrations/20190624150947_Initial.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/Migrations/20190624150947_Initial.cs
@@ -1,5 +1,6 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
+
// ReSharper disable RedundantArgumentDefaultValue
namespace Backend.Fx.EfCorePersistence.Tests.Migrations
@@ -9,8 +10,8 @@ public partial class Initial : Migration
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
- name: "Bloggers",
- columns: table => new
+ "Bloggers",
+ table => new
{
Id = table.Column(nullable: false),
CreatedOn = table.Column(nullable: false),
@@ -23,14 +24,11 @@ protected override void Up(MigrationBuilder migrationBuilder)
Bio = table.Column(nullable: true),
RowVersion = table.Column(rowVersion: true, nullable: true)
},
- constraints: table =>
- {
- table.PrimaryKey("PK_Bloggers", x => x.Id);
- });
+ constraints: table => { table.PrimaryKey("PK_Bloggers", x => x.Id); });
migrationBuilder.CreateTable(
- name: "Blogs",
- columns: table => new
+ "Blogs",
+ table => new
{
Id = table.Column(nullable: false),
CreatedOn = table.Column(nullable: false),
@@ -41,31 +39,25 @@ protected override void Up(MigrationBuilder migrationBuilder)
Name = table.Column(nullable: true),
RowVersion = table.Column(rowVersion: true, nullable: true)
},
- constraints: table =>
- {
- table.PrimaryKey("PK_Blogs", x => x.Id);
- });
+ constraints: table => { table.PrimaryKey("PK_Blogs", x => x.Id); });
migrationBuilder.CreateTable(
- name: "Tenants",
- columns: table => new
+ "Tenants",
+ table => new
{
Id = table.Column(nullable: false)
- .Annotation("Sqlite:Autoincrement", true),
+ .Annotation("Sqlite:Autoincrement", true),
Name = table.Column(nullable: false),
Description = table.Column(nullable: true),
IsDemoTenant = table.Column(nullable: false),
State = table.Column(nullable: false),
DefaultCultureName = table.Column(nullable: true)
},
- constraints: table =>
- {
- table.PrimaryKey("PK_Tenants", x => x.Id);
- });
+ constraints: table => { table.PrimaryKey("PK_Tenants", x => x.Id); });
migrationBuilder.CreateTable(
- name: "Posts",
- columns: table => new
+ "Posts",
+ table => new
{
Id = table.Column(nullable: false),
CreatedOn = table.Column(nullable: false),
@@ -82,32 +74,32 @@ protected override void Up(MigrationBuilder migrationBuilder)
{
table.PrimaryKey("PK_Posts", x => x.Id);
table.ForeignKey(
- name: "FK_Posts_Blogs_BlogId",
- column: x => x.BlogId,
- principalTable: "Blogs",
- principalColumn: "Id",
+ "FK_Posts_Blogs_BlogId",
+ x => x.BlogId,
+ "Blogs",
+ "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
- name: "IX_Posts_BlogId",
- table: "Posts",
- column: "BlogId");
+ "IX_Posts_BlogId",
+ "Posts",
+ "BlogId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
- name: "Bloggers");
+ "Bloggers");
migrationBuilder.DropTable(
- name: "Posts");
+ "Posts");
migrationBuilder.DropTable(
- name: "Tenants");
+ "Tenants");
migrationBuilder.DropTable(
- name: "Blogs");
+ "Blogs");
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/TestConfig.cs b/tests/Backend.Fx.EfCorePersistence.Tests/TestConfig.cs
index 92a3eeb0..6fde5b9a 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/TestConfig.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/TestConfig.cs
@@ -1,14 +1,17 @@
-using Backend.Fx.NLogLogging;
+using Backend.Fx.EfCorePersistence.Tests;
+using Backend.Fx.NLogLogging;
using MarcWittke.Xunit.AssemblyFixture;
+using Xunit;
-[assembly: Xunit.TestFramework("MarcWittke.Xunit.AssemblyFixture.XunitTestFrameworkWithAssemblyFixture", "MarcWittke.Xunit.AssemblyFixture")]
-[assembly: AssemblyFixture(typeof(Backend.Fx.EfCorePersistence.Tests.TestLoggingFixture))]
+[assembly: TestFramework("MarcWittke.Xunit.AssemblyFixture.XunitTestFrameworkWithAssemblyFixture", "MarcWittke.Xunit.AssemblyFixture")]
+[assembly: AssemblyFixture(typeof(TestLoggingFixture))]
namespace Backend.Fx.EfCorePersistence.Tests
{
public class TestLoggingFixture : LoggingFixture
{
public TestLoggingFixture() : base("Backend.Fx")
- { }
+ {
+ }
}
}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/TheDbContext.cs b/tests/Backend.Fx.EfCorePersistence.Tests/TheDbContext.cs
index c71e15e6..07fdd6b1 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/TheDbContext.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/TheDbContext.cs
@@ -1,7 +1,5 @@
-using System;
using System.Linq;
using Backend.Fx.EfCorePersistence.Tests.DummyImpl.Domain;
-using Backend.Fx.EfCorePersistence.Tests.DummyImpl.Persistence;
using Backend.Fx.EfCorePersistence.Tests.Fixtures;
using Microsoft.EntityFrameworkCore;
using Xunit;
@@ -10,64 +8,50 @@ namespace Backend.Fx.EfCorePersistence.Tests
{
public class TheDbContext
{
- private readonly DatabaseFixture _fixture;
- private static int _nextTenantId = 2675;
- private readonly int _tenantId = _nextTenantId++;
-
public TheDbContext()
{
_fixture = new SqliteDatabaseFixture();
_fixture.CreateDatabase();
}
+ private readonly DatabaseFixture _fixture;
+ private static int _nextTenantId = 2675;
+ private readonly int _tenantId = _nextTenantId++;
+
[Fact]
public void CanClearAndReplaceDependentEntites()
{
- using (DbSession dbs = _fixture.UseDbSession())
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
{
- using (var dbContext = new TestDbContext(dbs.OptionsBuilder.Options))
- {
- var blog = new Blog(1, "original blog") {TenantId = _tenantId};
- blog.Posts.Add(new Post(1, blog, "new name 1"));
- blog.Posts.Add(new Post(2, blog, "new name 2"));
- blog.Posts.Add(new Post(3, blog, "new name 3"));
- blog.Posts.Add(new Post(4, blog, "new name 4"));
- blog.Posts.Add(new Post(5, blog, "new name 5"));
- dbContext.Add(blog);
- dbContext.UpdateTrackingProperties("me", DateTime.Now);
- dbContext.SaveChanges();
- }
+ var blog = new Blog(1, "original blog") {TenantId = _tenantId};
+ blog.Posts.Add(new Post(1, blog, "new name 1"));
+ blog.Posts.Add(new Post(2, blog, "new name 2"));
+ blog.Posts.Add(new Post(3, blog, "new name 3"));
+ blog.Posts.Add(new Post(4, blog, "new name 4"));
+ blog.Posts.Add(new Post(5, blog, "new name 5"));
+ dbSession.DbContext.Add(blog);
+ }
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ Blog blog = dbSession.DbContext.Blogs.Include(b => b.Posts).Single(b => b.Id == 1);
+ blog.Posts.Clear();
+ blog.Posts.Add(new Post(6, blog, "new name 6"));
+ blog.Posts.Add(new Post(7, blog, "new name 7"));
+ blog.Posts.Add(new Post(8, blog, "new name 8"));
+ blog.Posts.Add(new Post(9, blog, "new name 9"));
+ blog.Posts.Add(new Post(10, blog, "new name 10"));
+ }
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ Blog blog = dbSession.DbContext.Blogs.Include(b => b.Posts).Single(b => b.Id == 1);
+
+ Assert.Equal(5, blog.Posts.Count);
- using (var dbContext = new TestDbContext(dbs.OptionsBuilder.Options))
- {
- Blog blog = dbContext.Blogs.Include(b => b.Posts).Single(b => b.Id == 1);
- blog.Posts.Clear();
- var post6 = new Post(6, blog, "new name 6");
- blog.Posts.Add(post6);
- blog.Posts.Add(new Post(7, blog, "new name 7"));
- blog.Posts.Add(new Post(8, blog, "new name 8"));
- blog.Posts.Add(new Post(9, blog, "new name 9"));
- blog.Posts.Add(new Post(10, blog, "new name 10"));
- dbContext.UpdateTrackingProperties("me", DateTime.Now);
- dbContext.SaveChanges();
- }
+ for (var i = 1; i <= 5; i++) Assert.DoesNotContain(blog.Posts, p => p.Id == i);
- using (var dbContext = new TestDbContext(dbs.OptionsBuilder.Options))
- {
- Blog blog = dbContext.Blogs.Include(b => b.Posts).Single(b => b.Id == 1);
-
- Assert.Equal(5, blog.Posts.Count);
-
- for (int i = 1; i <= 5; i++)
- {
- Assert.DoesNotContain(blog.Posts, p => p.Id == i);
- }
-
- for (int i = 6; i <= 10; i++)
- {
- Assert.Contains(blog.Posts, p => p.Id == i);
- }
- }
+ for (var i = 6; i <= 10; i++) Assert.Contains(blog.Posts, p => p.Id == i);
}
}
}
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/TheEfUnitOfWork.cs b/tests/Backend.Fx.EfCorePersistence.Tests/TheEfUnitOfWork.cs
deleted file mode 100644
index 7f0bf112..00000000
--- a/tests/Backend.Fx.EfCorePersistence.Tests/TheEfUnitOfWork.cs
+++ /dev/null
@@ -1,132 +0,0 @@
-using System;
-using System.Linq;
-using Backend.Fx.BuildingBlocks;
-using Backend.Fx.EfCorePersistence.Tests.DummyImpl.Domain;
-using Backend.Fx.EfCorePersistence.Tests.DummyImpl.Persistence;
-using Backend.Fx.EfCorePersistence.Tests.Fixtures;
-using Backend.Fx.Environment.Authentication;
-using Backend.Fx.Environment.DateAndTime;
-using Backend.Fx.Environment.MultiTenancy;
-using Backend.Fx.Patterns.Authorization;
-using Backend.Fx.Patterns.EventAggregation.Domain;
-using Backend.Fx.Patterns.EventAggregation.Integration;
-using Backend.Fx.Patterns.UnitOfWork;
-using FakeItEasy;
-using Microsoft.EntityFrameworkCore.Storage;
-using Xunit;
-
-namespace Backend.Fx.EfCorePersistence.Tests
-{
- public class TheEfUnitOfWork
- {
- private readonly DatabaseFixture _fixture;
- private static int _nextTenantId = 2675;
- private readonly int _tenantId = _nextTenantId++;
-
- public TheEfUnitOfWork()
- {
- _fixture = new SqliteDatabaseFixture();
- //_fixture = new SqlServerDatabaseFixture();
- _fixture.CreateDatabase();
- }
-
- [Fact]
- public void OpensTransaction()
- {
- using (DbSession dbs = _fixture.UseDbSession())
- {
- IUnitOfWork sut = dbs.BeginUnitOfWork();
- Assert.NotNull(sut.GetDbContext().Database.CurrentTransaction);
- Assert.Equal(dbs.Connection, sut.GetDbContext().Database.CurrentTransaction.GetDbTransaction().Connection);
-
- sut.GetDbContext().Add(new Blogger(333, "Metulsky", "Bratislav"));
- sut.Complete();
-
- Assert.Null(sut.GetDbTransaction());
- Assert.Throws(() => sut.GetDbContext().Database.CurrentTransaction.Commit()); // because sut.Complete() did it already
-
-
- sut = dbs.BeginUnitOfWork();
- Assert.NotNull(sut.GetDbContext().Set().SingleOrDefault(b => b.Id == 333 && b.FirstName == "Bratislav" && b.LastName == "Metulsky"));
- }
- }
-
- [Fact]
- public void RollsBackTransactionOnDisposal()
- {
- using (DbSession dbs = _fixture.UseDbSession())
- {
-
- IUnitOfWork sut = dbs.BeginUnitOfWork();
- sut.GetDbContext().Add(new Blogger(333, "Metulsky", "Bratislav"));
- sut.GetDbContext().SaveChanges();
- // no sut.Complete()
- sut.GetDbTransaction().Dispose();
- Assert.Throws(() =>
- sut.GetDbContext().Database.CurrentTransaction.Commit()); // because sut.Dispose() has already rolled back the open tx
-
- sut = dbs.BeginUnitOfWork();
-
- Assert.Null(sut.GetDbContext().Set().SingleOrDefault(b => b.Id == 333 && b.FirstName == "Bratislav" && b.LastName == "Metulsky"));
- }
- }
-
-
- private class AnEvent : IDomainEvent { }
-
- private class AnEventHandler : IDomainEventHandler
- {
- private readonly IRepository _blogRepository;
-
- public AnEventHandler(IRepository blogRepository)
- {
- _blogRepository = blogRepository;
- }
-
- public void Handle(AnEvent domainEvent)
- {
- _blogRepository.Add(new Blog(99999, "Created via Event Handling"));
- }
- }
-
- [Fact]
- public void FlushesAfterDomainEventHandling()
- {
- var fakeEventHandlerProvider = A.Fake();
- var domainEventAggregator = new DomainEventAggregator(fakeEventHandlerProvider);
-
-
- using (DbSession dbs = _fixture.UseDbSession())
- {
- var sut = new EfUnitOfWork(new FrozenClock(),
- CurrentIdentityHolder.CreateSystem(),
- domainEventAggregator,
- A.Fake(),
- // ReSharper disable once AccessToDisposedClosure
- new TestDbContext(dbs.OptionsBuilder.Options));
-
- sut.Begin();
- A.CallTo(() => fakeEventHandlerProvider.GetAllEventHandlers())
- .ReturnsLazily(() =>
- {
- // ReSharper disable once AccessToDisposedClosure
- var repo = new EfRepository(sut.DbContext, new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
- return new[] {new AnEventHandler(repo)};
- });
-
- domainEventAggregator.PublishDomainEvent(new AnEvent());
- Blog createdViaEvent = sut.DbContext.Set().Find(99999);
- Assert.Null(createdViaEvent);
- sut.Complete();
- }
-
- using (DbSession dbs = _fixture.UseDbSession())
- {
- IUnitOfWork sut = dbs.BeginUnitOfWork();
- Blog createdViaEvent = sut.GetDbContext().Set().Find(99999);
- Assert.NotNull(createdViaEvent);
- Assert.Equal("Created via Event Handling", createdViaEvent.Name);
- }
- }
- }
-}
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/TheReadonlyTransactionContext.cs b/tests/Backend.Fx.EfCorePersistence.Tests/TheReadonlyTransactionContext.cs
deleted file mode 100644
index 06865dfd..00000000
--- a/tests/Backend.Fx.EfCorePersistence.Tests/TheReadonlyTransactionContext.cs
+++ /dev/null
@@ -1,69 +0,0 @@
-using System;
-using Backend.Fx.EfCorePersistence.Tests.DummyImpl.Domain;
-using Backend.Fx.EfCorePersistence.Tests.Fixtures;
-using Backend.Fx.Patterns.UnitOfWork;
-using Xunit;
-
-namespace Backend.Fx.EfCorePersistence.Tests
-{
- public class TheReadonlyTransactionContext
- {
- private readonly DatabaseFixture _fixture;
-
- public TheReadonlyTransactionContext()
- {
- _fixture = new SqliteDatabaseFixture();
- //_fixture = new SqlServerDatabaseFixture();
- _fixture.CreateDatabase();
- }
-
- [Fact]
- public void OpensTransaction()
- {
- using(DbSession dbs = _fixture.UseDbSession())
- {
- IUnitOfWork sut = dbs.BeginUnitOfWork(true);
- Assert.NotNull(sut.GetDbContext());
- Assert.NotNull(sut.GetDbContext().Database.CurrentTransaction);
- sut.Complete();
- Assert.Throws(() => sut.GetDbContext().Database.CurrentTransaction.Commit());
- }
- }
-
- [Fact]
- public void RollbacksTransactionOnComplete()
- {
- using (DbSession dbs = _fixture.UseDbSession())
- {
- IUnitOfWork sut = dbs.BeginUnitOfWork(true);
- sut.GetDbContext().Add(new Blogger(334, "Bratislav", "Metulsky"));
- sut.Complete();
- Assert.Throws(() => sut.GetDbContext().Database.CurrentTransaction.Commit());
- }
-
- using (DbSession dbs = _fixture.UseDbSession())
- {
- IUnitOfWork sut = dbs.BeginUnitOfWork();
- Assert.Empty(sut.GetDbContext().Set());
- }
- }
-
- [Fact]
- public void RollbacksTransactionOnDisposal()
- {
- using (DbSession dbs = _fixture.UseDbSession())
- {
- IUnitOfWork sut = dbs.BeginUnitOfWork(true);
- sut.GetDbContext().Add(new Blogger(335, "Bratislav", "Metulsky"));
- sut.GetDbTransaction().Dispose();
- Assert.Throws(() => sut.GetDbContext().Database.CurrentTransaction.Commit());
- }
-
- using (DbSession dbs = _fixture.UseDbSession())
- {
- IUnitOfWork sut = dbs.BeginUnitOfWork();
- Assert.Empty(sut.GetDbContext().Set());
- }
- }
- }
-}
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/TheRepositoryOfComposedAggregate.cs b/tests/Backend.Fx.EfCorePersistence.Tests/TheRepositoryOfComposedAggregate.cs
index c2d55194..a0de99dd 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/TheRepositoryOfComposedAggregate.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/TheRepositoryOfComposedAggregate.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Data;
using System.Linq;
using Backend.Fx.EfCorePersistence.Tests.DummyImpl.Domain;
using Backend.Fx.EfCorePersistence.Tests.DummyImpl.Persistence;
@@ -9,7 +10,6 @@
using Backend.Fx.Extensions;
using Backend.Fx.Patterns.Authorization;
using Backend.Fx.Patterns.IdGeneration;
-using Backend.Fx.Patterns.UnitOfWork;
using FakeItEasy;
using Xunit;
@@ -17,15 +17,6 @@ namespace Backend.Fx.EfCorePersistence.Tests
{
public class TheRepositoryOfComposedAggregate
{
- private static int _nextTenantId = 57839;
- private static int _nextId = 1;
- private readonly int _tenantId = _nextTenantId++;
- private readonly IEqualityComparer _tolerantDateTimeComparer = new TolerantDateTimeComparer(TimeSpan.FromMilliseconds(5000));
- private readonly IEntityIdGenerator _idGenerator = A.Fake();
- private readonly DatabaseFixture _fixture;
- private readonly IClock _clock = new FrozenClock();
-
-
public TheRepositoryOfComposedAggregate()
{
A.CallTo(() => _idGenerator.NextId()).ReturnsLazily(() => _nextId++);
@@ -34,108 +25,128 @@ public TheRepositoryOfComposedAggregate()
_fixture.CreateDatabase();
}
- [Fact]
- public void CanCreate()
+ private static int _nextTenantId = 57839;
+ private static int _nextId = 1;
+ private readonly int _tenantId = _nextTenantId++;
+ private readonly IEqualityComparer _tolerantDateTimeComparer = new TolerantDateTimeComparer(TimeSpan.FromMilliseconds(5000));
+ private readonly IEntityIdGenerator _idGenerator = A.Fake();
+ private readonly DatabaseFixture _fixture;
+
+ private int CreateBlogWithPost(IDbConnection dbConnection, int postCount = 1)
{
- using (DbSession dbs = _fixture.UseDbSession())
{
-
- {
- int count = dbs.Connection.ExecuteScalar("SELECT count(*) FROM Blogs");
- Assert.Equal(0, count);
-
- count = dbs.Connection.ExecuteScalar("SELECT count(*) FROM Posts");
- Assert.Equal(0, count);
- }
-
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork(clock:_clock);
- var sut = new EfRepository(uow.GetDbContext(), new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
- var blog = new Blog(_idGenerator.NextId(), "my blog");
- blog.AddPost(_idGenerator, "my post");
- sut.Add(blog);
- uow.Complete();
- }
-
-
- {
- int count = dbs.Connection.ExecuteScalar("SELECT count(*) FROM Blogs");
- Assert.Equal(1, count);
-
- count = dbs.Connection.ExecuteScalar("SELECT count(*) FROM Posts");
- Assert.Equal(1, count);
- }
+ var blogId = _nextId++;
+ dbConnection.ExecuteNonQuery(
+ $"INSERT INTO Blogs (Id, TenantId, Name, CreatedOn, CreatedBy) VALUES ({blogId}, {CurrentTenantIdHolder.Create(_tenantId).Current.Value}, 'my blog', CURRENT_TIMESTAMP, 'persistence test')");
+ var count = dbConnection.ExecuteScalar("SELECT count(*) FROM Blogs");
+ Assert.Equal(1, count);
+
+ for (var i = 0; i < postCount; i++)
+ dbConnection.ExecuteNonQuery(
+ $"INSERT INTO Posts (Id, BlogId, Name, TargetAudience_IsPublic, TargetAudience_Culture, CreatedOn, CreatedBy) VALUES ({_nextId++}, {blogId}, 'my post {i:00}', '1', 'de-DE', CURRENT_TIMESTAMP, 'persistence test')");
+
+ return blogId;
}
}
+ //FAILING!!!!
+ // this shows, that ValueObjects treated as OwnedTypes are not supported very well
+ //[Fact]
+ //public void CanUpdateDependantValueObject()
+ //{
+ // using (DbSession dbs = _fixture.UseDbSession())
+ // {
+ // int id = CreateBlogWithPost(dbSession.DbConnection, 10);
+ // Post post;
+
+ // using (var uow = dbs.UseUnitOfWork(_clock))
+ // {
+ // var sut = new EfRepository(uow.DbContext, new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId),
+ // new AllowAll());
+ // var blog = sut.Single(id);
+ // post = blog.Posts.First();
+ // post.TargetAudience = new TargetAudience{Culture = "es-AR", IsPublic = false};
+ // uow.Complete();
+ // }
+
+ //
+ // {
+ // string culture = dbSession.DbConnection.ExecuteScalar($"SELECT TargetAudience_Culture ame FROM Posts where id = {post.Id}");
+ // Assert.Equal("es-AR", culture);
+
+ // string strChangedOn = dbSession.DbConnection.ExecuteScalar($"SELECT ChangedOn FROM Posts where id = {post.Id}");
+ // DateTime changedOn = DateTime.Parse(strChangedOn);
+ // Assert.Equal(_clock.UtcNow, changedOn, new TolerantDateTimeComparer(TimeSpan.FromMilliseconds(500)));
+ // }
+ // }
+ //}
+
[Fact]
- public void CanRead()
+ public void CanAddDependent()
{
- using (DbSession dbs = _fixture.UseDbSession())
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
{
- var id = CreateBlogWithPost(dbs);
- Blog blog;
-
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork(clock:_clock);
- var sut = new EfRepository(uow.GetDbContext(), new BlogMapping(),
- CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
- blog = sut.Single(id);
- uow.Complete();
- }
-
-
- {
- Assert.NotNull(blog);
- Assert.Equal(id, blog.Id);
- Assert.Equal("my blog", blog.Name);
- Assert.NotEmpty(blog.Posts);
- }
+ var id = CreateBlogWithPost(dbSession.DbConnection, 10);
+ var sut = new EfRepository(dbSession.DbContext, new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
+ Blog blog = sut.Single(id);
+ blog.Posts.Add(new Post(_idGenerator.NextId(), blog, "added"));
+ }
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var count = dbSession.DbConnection.ExecuteScalar("SELECT count(*) FROM Posts");
+ Assert.Equal(11, count);
}
}
[Fact]
- public void CanUpdate()
+ public void CanCreate()
{
- using (DbSession dbs = _fixture.UseDbSession())
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var count = dbSession.DbConnection.ExecuteScalar("SELECT count(*) FROM Blogs");
+ Assert.Equal(0, count);
+
+ count = dbSession.DbConnection.ExecuteScalar("SELECT count(*) FROM Posts");
+ Assert.Equal(0, count);
+ }
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
{
- var id = CreateBlogWithPost(dbs);
-
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork(clock:_clock);
- var sut = new EfRepository(uow.GetDbContext(), new BlogMapping(),
- CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
- Blog blog = sut.Single(id);
- blog.Modify("modified");
- uow.Complete();
- }
-
- Assert.Equal(1, dbs.Connection.ExecuteScalar("SELECT count(*) FROM Blogs"));
- Assert.Equal(id, dbs.Connection.ExecuteScalar("SELECT Id FROM Blogs LIMIT 1"));
- Assert.Equal("modified", dbs.Connection.ExecuteScalar("SELECT Name FROM Blogs LIMIT 1"));
- Assert.Equal("modified", dbs.Connection.ExecuteScalar("SELECT Name FROM Posts LIMIT 1"));
+ var sut = new EfRepository(dbSession.DbContext, new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
+ var blog = new Blog(_idGenerator.NextId(), "my blog");
+ blog.AddPost(_idGenerator, "my post");
+ sut.Add(blog);
+ }
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var count = dbSession.DbConnection.ExecuteScalar("SELECT count(*) FROM Blogs");
+ Assert.Equal(1, count);
+
+ count = dbSession.DbConnection.ExecuteScalar("SELECT count(*) FROM Posts");
+ Assert.Equal(1, count);
}
}
[Fact]
public void CanDelete()
{
- using (DbSession dbs = _fixture.UseDbSession())
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
{
- var id = CreateBlogWithPost(dbs);
+ var id = CreateBlogWithPost(dbSession.DbConnection);
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork(clock:_clock);
- var sut = new EfRepository(uow.GetDbContext(), new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
- Blog blog = sut.Single(id);
- sut.Delete(blog);
- uow.Complete();
- }
+ var sut = new EfRepository(dbSession.DbContext, new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
+ Blog blog = sut.Single(id);
+ sut.Delete(blog);
+ }
- int count = dbs.Connection.ExecuteScalar("SELECT count(*) FROM Blogs");
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var count = dbSession.DbConnection.ExecuteScalar("SELECT count(*) FROM Blogs");
Assert.Equal(0, count);
- count = dbs.Connection.ExecuteScalar("SELECT count(*) FROM Posts");
+ count = dbSession.DbConnection.ExecuteScalar("SELECT count(*) FROM Posts");
Assert.Equal(0, count);
}
}
@@ -143,189 +154,172 @@ public void CanDelete()
[Fact]
public void CanDeleteDependent()
{
- using (DbSession dbs = _fixture.UseDbSession())
+ int id;
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
{
- int id = CreateBlogWithPost(dbs, 10);
-
- {
- int count = dbs.Connection.ExecuteScalar("SELECT count(*) FROM Posts");
- Assert.Equal(10, count);
- }
-
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork(clock:_clock);
- var sut = new EfRepository(uow.GetDbContext(), new BlogMapping(),
- CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
- Blog blog = sut.Single(id);
- Post firstPost = blog.Posts.First();
- firstPost.SetName("sadfasfsadf");
- blog.Posts.Remove(firstPost);
- uow.Complete();
- }
-
- {
- var count = dbs.Connection.ExecuteScalar("SELECT count(*) FROM Posts");
- Assert.Equal(9, count);
- }
+ id = CreateBlogWithPost(dbSession.DbConnection, 10);
+ var count = dbSession.DbConnection.ExecuteScalar("SELECT count(*) FROM Posts");
+ Assert.Equal(10, count);
+ }
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var sut = new EfRepository(dbSession.DbContext, new BlogMapping(),
+ CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
+ Blog blog = sut.Single(id);
+ Post firstPost = blog.Posts.First();
+ firstPost.SetName("sadfasfsadf");
+ blog.Posts.Remove(firstPost);
+ }
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var count = dbSession.DbConnection.ExecuteScalar("SELECT count(*) FROM Posts");
+ Assert.Equal(9, count);
}
}
+
[Fact]
- public void CanUpdateDependant()
+ public void CanRead()
{
- using (DbSession dbs = _fixture.UseDbSession())
+ int id;
+ Blog blog;
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
{
- int id = CreateBlogWithPost(dbs, 10);
- Post post;
-
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork(clock:_clock);
- var sut = new EfRepository(uow.GetDbContext(), new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId),
- new AllowAll());
- Blog blog = sut.Single(id);
- post = blog.Posts.First();
- post.SetName("modified");
- uow.Complete();
- }
-
-
- {
- string name = dbs.Connection.ExecuteScalar($"SELECT name FROM Posts where id = {post.Id}");
- Assert.Equal("modified", name);
-
- string strChangedOn = dbs.Connection.ExecuteScalar($"SELECT changedon FROM Posts where id = {post.Id}");
- DateTime changedOn = DateTime.Parse(strChangedOn);
- Assert.Equal(_clock.UtcNow, changedOn, new TolerantDateTimeComparer(TimeSpan.FromMilliseconds(500)));
- }
+ id = CreateBlogWithPost(dbSession.DbConnection);
}
- }
- //FAILING!!!!
- // this shows, that ValueObjects treated as OwnedTypes are not supported very well
- //[Fact]
- //public void CanUpdateDependantValueObject()
- //{
- // using (DbSession dbs = _fixture.UseDbSession())
- // {
- // int id = CreateBlogWithPost(dbs, 10);
- // Post post;
-
- // using (var uow = dbs.UseUnitOfWork(_clock))
- // {
- // var sut = new EfRepository(uow.DbContext, new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId),
- // new AllowAll());
- // var blog = sut.Single(id);
- // post = blog.Posts.First();
- // post.TargetAudience = new TargetAudience{Culture = "es-AR", IsPublic = false};
- // uow.Complete();
- // }
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var sut = new EfRepository(dbSession.DbContext, new BlogMapping(),
+ CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
+ blog = sut.Single(id);
+ }
- //
- // {
- // string culture = dbs.Connection.ExecuteScalar($"SELECT TargetAudience_Culture ame FROM Posts where id = {post.Id}");
- // Assert.Equal("es-AR", culture);
+ Assert.NotNull(blog);
+ Assert.Equal(id, blog.Id);
+ Assert.Equal("my blog", blog.Name);
+ Assert.NotEmpty(blog.Posts);
+ }
- // string strChangedOn = dbs.Connection.ExecuteScalar($"SELECT ChangedOn FROM Posts where id = {post.Id}");
- // DateTime changedOn = DateTime.Parse(strChangedOn);
- // Assert.Equal(_clock.UtcNow, changedOn, new TolerantDateTimeComparer(TimeSpan.FromMilliseconds(500)));
- // }
- // }
- //}
[Fact]
- public void CanAddDependent()
+ public void CanReplaceDependentCollection()
{
- using (DbSession dbs = _fixture.UseDbSession())
+ int id;
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
{
- int id = CreateBlogWithPost(dbs, 10);
-
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork(clock:_clock);
- var sut = new EfRepository(uow.GetDbContext(), new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
- Blog blog = sut.Single(id);
- blog.Posts.Add(new Post(_idGenerator.NextId(), blog, "added"));
- uow.Complete();
-
- int count = dbs.Connection.ExecuteScalar("SELECT count(*) FROM Posts");
- Assert.Equal(11, count);
- }
+ id = CreateBlogWithPost(dbSession.DbConnection, 10);
}
- }
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var sut = new EfRepository(dbSession.DbContext, new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
+ Blog blog = sut.Single(id);
+ blog.Posts.Clear();
+ blog.Posts.Add(new Post(_idGenerator.NextId(), blog, "new name 1"));
+ blog.Posts.Add(new Post(_idGenerator.NextId(), blog, "new name 2"));
+ blog.Posts.Add(new Post(_idGenerator.NextId(), blog, "new name 3"));
+ blog.Posts.Add(new Post(_idGenerator.NextId(), blog, "new name 4"));
+ blog.Posts.Add(new Post(_idGenerator.NextId(), blog, "new name 5"));
+ }
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var count = dbSession.DbConnection.ExecuteScalar("SELECT count(*) FROM Posts");
+ Assert.Equal(5, count);
+ }
+ }
[Fact]
- public void CanReplaceDependentCollection()
+ public void CanUpdate()
{
- using (DbSession dbs = _fixture.UseDbSession())
+ int id;
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
{
- var id = CreateBlogWithPost(dbs, 10);
-
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork(clock:_clock);
- var sut = new EfRepository(uow.GetDbContext(), new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
- Blog blog = sut.Single(id);
- blog.Posts.Clear();
- blog.Posts.Add(new Post(_idGenerator.NextId(), blog, "new name 1"));
- blog.Posts.Add(new Post(_idGenerator.NextId(), blog, "new name 2"));
- blog.Posts.Add(new Post(_idGenerator.NextId(), blog, "new name 3"));
- blog.Posts.Add(new Post(_idGenerator.NextId(), blog, "new name 4"));
- blog.Posts.Add(new Post(_idGenerator.NextId(), blog, "new name 5"));
- uow.Complete();
- }
-
-
- {
- int count = dbs.Connection.ExecuteScalar("SELECT count(*) FROM Posts");
- Assert.Equal(5, count);
- }
+ id = CreateBlogWithPost(dbSession.DbConnection);
+ }
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var sut = new EfRepository(dbSession.DbContext, new BlogMapping(),
+ CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
+ Blog blog = sut.Single(id);
+ blog.Modify("modified");
+ }
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ Assert.Equal(1, dbSession.DbConnection.ExecuteScalar("SELECT count(*) FROM Blogs"));
+ Assert.Equal(id, dbSession.DbConnection.ExecuteScalar("SELECT Id FROM Blogs LIMIT 1"));
+ Assert.Equal("modified", dbSession.DbConnection.ExecuteScalar("SELECT Name FROM Blogs LIMIT 1"));
+ Assert.Equal("modified", dbSession.DbConnection.ExecuteScalar("SELECT Name FROM Posts LIMIT 1"));
}
}
[Fact]
- public void UpdatesAggregateTrackingPropertiesOnDeleteOfDependant()
+ public void CanUpdateDependant()
{
- using (DbSession dbs = _fixture.UseDbSession())
+ var clock = new AdjustableClock(new WallClock());
+ clock.OverrideUtcNow(new DateTime(2020, 01, 20, 20, 30, 40));
+
+ int id;
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession(clock: clock))
{
- int id = CreateBlogWithPost(dbs, 10);
-
- DateTime expectedModifiedOn = _clock.UtcNow.AddHours(1);
- _clock.OverrideUtcNow(expectedModifiedOn);
-
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork(clock:_clock);
- var sut = new EfRepository(uow.GetDbContext(), new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
- Blog b = sut.Single(id);
- b.Posts.Remove(b.Posts.First());
- uow.Complete();
- }
-
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork(clock:_clock);
- Blog blog = uow.GetDbContext().Set().Find(id);
- Assert.NotNull(blog.ChangedOn);
- Assert.Equal(expectedModifiedOn, blog.ChangedOn.Value, _tolerantDateTimeComparer);
- }
+ id = CreateBlogWithPost(dbSession.DbConnection, 10);
+ }
+
+ Post post;
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession(clock: clock))
+ {
+ var sut = new EfRepository(dbSession.DbContext, new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId),
+ new AllowAll());
+ Blog blog = sut.Single(id);
+ post = blog.Posts.First();
+ post.SetName("modified");
+ }
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession(clock: clock))
+ {
+ var name = dbSession.DbConnection.ExecuteScalar($"SELECT name FROM Posts where id = {post.Id}");
+ Assert.Equal("modified", name);
+
+ var strChangedOn = dbSession.DbConnection.ExecuteScalar($"SELECT changedon FROM Posts where id = {post.Id}");
+ DateTime changedOn = DateTime.Parse(strChangedOn);
+ Assert.Equal(clock.UtcNow, changedOn, new TolerantDateTimeComparer(TimeSpan.FromMilliseconds(500)));
}
}
- private int CreateBlogWithPost(DbSession dbs, int postCount = 1)
+ [Fact]
+ public void UpdatesAggregateTrackingPropertiesOnDeleteOfDependant()
{
+ var clock = new AdjustableClock(new WallClock());
+ clock.OverrideUtcNow(new DateTime(2020, 01, 20, 20, 30, 40));
+ int id;
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession(clock:clock))
{
- int blogId = _nextId++;
- dbs.Connection.ExecuteNonQuery(
- $"INSERT INTO Blogs (Id, TenantId, Name, CreatedOn, CreatedBy) VALUES ({blogId}, {CurrentTenantIdHolder.Create(_tenantId).Current.Value}, 'my blog', CURRENT_TIMESTAMP, 'persistence test')");
- int count = dbs.Connection.ExecuteScalar("SELECT count(*) FROM Blogs");
- Assert.Equal(1, count);
+ id = CreateBlogWithPost(dbSession.DbConnection, 10);
+ }
- for (int i = 0; i < postCount; i++)
- {
- dbs.Connection.ExecuteNonQuery(
- $"INSERT INTO Posts (Id, BlogId, Name, TargetAudience_IsPublic, TargetAudience_Culture, CreatedOn, CreatedBy) VALUES ({_nextId++}, {blogId}, 'my post {i:00}', '1', 'de-DE', CURRENT_TIMESTAMP, 'persistence test')");
- }
+ DateTime expectedModifiedOn = clock.Advance(TimeSpan.FromHours(1));
- return blogId;
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession(clock:clock))
+ {
+ var sut = new EfRepository(dbSession.DbContext, new BlogMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
+ Blog b = sut.Single(id);
+ b.Posts.Remove(b.Posts.First());
+ }
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession(clock:clock))
+ {
+ Blog blog = dbSession.DbContext.Set().Find(id);
+ Assert.NotNull(blog.ChangedOn);
+ Assert.Equal(expectedModifiedOn, blog.ChangedOn.Value, _tolerantDateTimeComparer);
}
}
}
-}
+}
\ No newline at end of file
diff --git a/tests/Backend.Fx.EfCorePersistence.Tests/TheRepositoryOfPlainAggregate.cs b/tests/Backend.Fx.EfCorePersistence.Tests/TheRepositoryOfPlainAggregate.cs
index 0d591e4c..79d0818e 100644
--- a/tests/Backend.Fx.EfCorePersistence.Tests/TheRepositoryOfPlainAggregate.cs
+++ b/tests/Backend.Fx.EfCorePersistence.Tests/TheRepositoryOfPlainAggregate.cs
@@ -6,17 +6,12 @@
using Backend.Fx.Environment.MultiTenancy;
using Backend.Fx.Extensions;
using Backend.Fx.Patterns.Authorization;
-using Backend.Fx.Patterns.UnitOfWork;
using Xunit;
namespace Backend.Fx.EfCorePersistence.Tests
{
public class TheRepositoryOfPlainAggregate
{
- private static int _nextTenantId = 12312;
- private readonly int _tenantId = _nextTenantId++;
- private readonly DatabaseFixture _fixture;
-
public TheRepositoryOfPlainAggregate()
{
//_fixture = new SqlServerDatabaseFixture();
@@ -24,81 +19,74 @@ public TheRepositoryOfPlainAggregate()
_fixture.CreateDatabase();
}
+ private static int _nextTenantId = 12312;
+ private readonly int _tenantId = _nextTenantId++;
+ private readonly DatabaseFixture _fixture;
+
[Fact]
public void CanCreate()
{
- using (var dbs = _fixture.UseDbSession())
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
{
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork();
- var repo = new EfRepository(uow.GetDbContext(), new BloggerMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
- repo.Add(new Blogger(345, "Metulsky", "Bratislav"));
- uow.Complete();
- }
+ var repo = new EfRepository(dbSession.DbContext, new BloggerMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
+ repo.Add(new Blogger(345, "Metulsky", "Bratislav"));
+ }
-
- {
- int count = dbs.Connection.ExecuteScalar("SELECT Count(*) FROM Bloggers");
- Assert.Equal(1L, count);
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var count = dbSession.DbConnection.ExecuteScalar("SELECT Count(*) FROM Bloggers");
+ Assert.Equal(1L, count);
- count = dbs.Connection.ExecuteScalar($"SELECT Count(*) FROM Bloggers WHERE FirstName = 'Bratislav' AND LastName = 'Metulsky' AND TenantId = '{_tenantId}'");
- Assert.Equal(1L, count);
- }
+ count = dbSession.DbConnection.ExecuteScalar(
+ $"SELECT Count(*) FROM Bloggers WHERE FirstName = 'Bratislav' AND LastName = 'Metulsky' AND TenantId = '{_tenantId}'");
+ Assert.Equal(1L, count);
}
}
[Fact]
- public void CanRead()
+ public void CanDelete()
{
- using (var dbs = _fixture.UseDbSession())
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
{
-
- {
- dbs.Connection.ExecuteNonQuery(
- "INSERT INTO Bloggers (Id, TenantId, CreatedOn, CreatedBy, FirstName, LastName, Bio) " +
- $"VALUES (444, {_tenantId}, '2012-05-12 23:12:09', 'the test', 'Bratislav', 'Metulsky', 'whatever')");
- }
-
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork();
- var repo = new EfRepository(uow.GetDbContext(), new BloggerMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
- Blogger bratislavMetulsky = repo.Single(444);
- Assert.Equal(_tenantId, bratislavMetulsky.TenantId);
- Assert.Equal("the test", bratislavMetulsky.CreatedBy);
- Assert.Equal(new DateTime(2012, 05, 12, 23, 12, 09), bratislavMetulsky.CreatedOn);
- Assert.Equal("Bratislav", bratislavMetulsky.FirstName);
- Assert.Equal("Metulsky", bratislavMetulsky.LastName);
- Assert.Equal("whatever", bratislavMetulsky.Bio);
- uow.Complete();
- }
+ dbSession.DbConnection.ExecuteNonQuery(
+ "INSERT INTO Bloggers (Id, TenantId, CreatedOn, CreatedBy, FirstName, LastName, Bio) " +
+ $"VALUES (555, {_tenantId}, '2012-05-12 23:12:09', 'the test', 'Bratislav', 'Metulsky', 'whatever')");
+ }
+
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var repo = new EfRepository(dbSession.DbContext, new BloggerMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
+ Blogger bratislavMetulsky = repo.Single(555);
+ repo.Delete(bratislavMetulsky);
+ }
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var count = dbSession.DbConnection.ExecuteScalar("SELECT Count(*) FROM Bloggers");
+ Assert.Equal(0L, count);
}
}
+
[Fact]
- public void CanDelete()
+ public void CanRead()
{
- using (var dbs = _fixture.UseDbSession())
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
{
-
- {
- dbs.Connection.ExecuteNonQuery(
- "INSERT INTO Bloggers (Id, TenantId, CreatedOn, CreatedBy, FirstName, LastName, Bio) " +
- $"VALUES (555, {_tenantId}, '2012-05-12 23:12:09', 'the test', 'Bratislav', 'Metulsky', 'whatever')");
- }
+ dbSession.DbConnection.ExecuteNonQuery(
+ "INSERT INTO Bloggers (Id, TenantId, CreatedOn, CreatedBy, FirstName, LastName, Bio) " +
+ $"VALUES (444, {_tenantId}, '2012-05-12 23:12:09', 'the test', 'Bratislav', 'Metulsky', 'whatever')");
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork();
- var repo = new EfRepository(uow.GetDbContext(), new BloggerMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
- Blogger bratislavMetulsky = repo.Single(555);
- repo.Delete(bratislavMetulsky);
- uow.Complete();
- }
-
{
- int count = dbs.Connection.ExecuteScalar("SELECT Count(*) FROM Bloggers");
- Assert.Equal(0L, count);
+ var repo = new EfRepository(dbSession.DbContext, new BloggerMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
+ Blogger bratislavMetulsky = repo.Single(444);
+ Assert.Equal(_tenantId, bratislavMetulsky.TenantId);
+ Assert.Equal("the test", bratislavMetulsky.CreatedBy);
+ Assert.Equal(new DateTime(2012, 05, 12, 23, 12, 09), bratislavMetulsky.CreatedOn);
+ Assert.Equal("Bratislav", bratislavMetulsky.FirstName);
+ Assert.Equal("Metulsky", bratislavMetulsky.LastName);
+ Assert.Equal("whatever", bratislavMetulsky.Bio);
}
}
}
@@ -106,47 +94,39 @@ public void CanDelete()
[Fact]
public void CanUpdate()
{
- using (var dbs = _fixture.UseDbSession())
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
{
-
- {
- dbs.Connection.ExecuteNonQuery(
- "INSERT INTO Bloggers (Id, TenantId, CreatedOn, CreatedBy, FirstName, LastName, Bio) " +
- $"VALUES (456, {_tenantId}, '2012-05-12 23:12:09', 'the test', 'Bratislav', 'Metulsky', 'whatever')");
- }
+ dbSession.DbConnection.ExecuteNonQuery(
+ "INSERT INTO Bloggers (Id, TenantId, CreatedOn, CreatedBy, FirstName, LastName, Bio) " +
+ $"VALUES (456, {_tenantId}, '2012-05-12 23:12:09', 'the test', 'Bratislav', 'Metulsky', 'whatever')");
+ }
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork();
- var repo = new EfRepository(uow.GetDbContext(), new BloggerMapping(),
- CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
- Blogger bratislavMetulsky = repo.Single(456);
- bratislavMetulsky.FirstName = "Johnny";
- bratislavMetulsky.LastName = "Flash";
- bratislavMetulsky.Bio = "Der lustige Clown";
- uow.Complete();
- }
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
+ {
+ var repo = new EfRepository(dbSession.DbContext, new BloggerMapping(),
+ CurrentTenantIdHolder.Create(_tenantId), new AllowAll());
+ Blogger bratislavMetulsky = repo.Single(456);
+ bratislavMetulsky.FirstName = "Johnny";
+ bratislavMetulsky.LastName = "Flash";
+ bratislavMetulsky.Bio = "Der lustige Clown";
}
- using (var dbs = _fixture.UseDbSession())
+ using (TestDbSession dbSession = _fixture.CreateTestDbSession())
{
-
- {
- var count = dbs.Connection.ExecuteScalar(
- $"SELECT Count(*) FROM Bloggers WHERE FirstName = 'Johnny' AND LastName = 'Flash' AND TenantId = '{_tenantId}'");
- Assert.Equal(1L, count);
- }
+ var count = dbSession.DbConnection.ExecuteScalar(
+ $"SELECT Count(*) FROM Bloggers WHERE FirstName = 'Johnny' AND LastName = 'Flash' AND TenantId = '{_tenantId}'");
+ Assert.Equal(1L, count);
+ }
- {
- IUnitOfWork uow = dbs.BeginUnitOfWork();
- var repo = new EfRepository(uow.GetDbContext(), new BloggerMapping(), CurrentTenantIdHolder.Create(_tenantId), new AllowAll