Adds Decorator, wip
This commit is contained in:
parent
a86a2d7ade
commit
49f9b99ee1
9 changed files with 112 additions and 45 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using System.Diagnostics;
|
||||||
using Microsoft.ApplicationInsights.DataContracts;
|
using Microsoft.ApplicationInsights.DataContracts;
|
||||||
using Microsoft.ApplicationInsights.Extensibility;
|
using Microsoft.ApplicationInsights.Extensibility;
|
||||||
|
|
||||||
|
|
@ -6,19 +7,24 @@ namespace PlanTempus.Core.Database;
|
||||||
|
|
||||||
public class DatabaseScope : IDisposable
|
public class DatabaseScope : IDisposable
|
||||||
{
|
{
|
||||||
private readonly IOperationHolder<DependencyTelemetry> _operation;
|
internal readonly IOperationHolder<DependencyTelemetry> _operation;
|
||||||
|
private readonly Stopwatch _stopwatch;
|
||||||
|
|
||||||
public DatabaseScope(IDbConnection connection, IOperationHolder<DependencyTelemetry> operation)
|
public DatabaseScope(IDbConnection connection, IOperationHolder<DependencyTelemetry> operation)
|
||||||
{
|
{
|
||||||
Connection = connection;
|
Connection = connection;
|
||||||
_operation = operation;
|
_operation = operation;
|
||||||
_operation.Telemetry.Success = true;
|
_operation.Telemetry.Success = true;
|
||||||
|
_stopwatch = Stopwatch.StartNew();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDbConnection Connection { get; }
|
public IDbConnection Connection { get; }
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
_stopwatch.Stop();
|
||||||
|
_operation.Telemetry.Duration = _stopwatch.Elapsed;
|
||||||
|
|
||||||
_operation.Dispose();
|
_operation.Dispose();
|
||||||
Connection.Dispose();
|
Connection.Dispose();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
14
Core/ModuleRegistry/SecurityModule.cs
Normal file
14
Core/ModuleRegistry/SecurityModule.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
using Autofac;
|
||||||
|
using PlanTempus.Core.SeqLogging;
|
||||||
|
|
||||||
|
namespace PlanTempus.Core.ModuleRegistry
|
||||||
|
{
|
||||||
|
public class SecurityModule : Module
|
||||||
|
{
|
||||||
|
protected override void Load(ContainerBuilder builder)
|
||||||
|
{
|
||||||
|
builder.RegisterType<SecureTokenizer>()
|
||||||
|
.As<ISecureTokenizer>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,9 +5,9 @@ namespace PlanTempus.Components;
|
||||||
|
|
||||||
public class CommandHandler(IComponentContext context) : ICommandHandler
|
public class CommandHandler(IComponentContext context) : ICommandHandler
|
||||||
{
|
{
|
||||||
public Task<TCommandResult> Handle<TCommand, TCommandResult>(TCommand command)
|
public async Task<TCommandResult> Handle<TCommand, TCommandResult>(TCommand command)
|
||||||
{
|
{
|
||||||
var handler = context.Resolve<ICommandHandler<TCommand, TCommandResult>>();
|
var handler = context.Resolve<ICommandHandler<TCommand, TCommandResult>>();
|
||||||
return handler.Handle(command);
|
return await handler.Handle(command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
15
PlanTempus.Components/ICommandHandler.cs
Normal file
15
PlanTempus.Components/ICommandHandler.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
namespace PlanTempus.Components;
|
||||||
|
|
||||||
|
public interface ICommandHandler
|
||||||
|
{
|
||||||
|
Task<TCommandResult> Handle<TCommand, TCommandResult>(TCommand command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ICommandHandler<in T, TResult>
|
||||||
|
{
|
||||||
|
Task<TResult> Handle(T input);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ICommandHandlerDecorator
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Autofac;
|
using Autofac;
|
||||||
|
using PlanTempus.Components.Users.Create;
|
||||||
using PlanTempus.Core.SeqLogging;
|
using PlanTempus.Core.SeqLogging;
|
||||||
|
|
||||||
namespace PlanTempus.Components.ModuleRegistry
|
namespace PlanTempus.Components.ModuleRegistry
|
||||||
|
|
@ -9,15 +10,34 @@ namespace PlanTempus.Components.ModuleRegistry
|
||||||
|
|
||||||
protected override void Load(ContainerBuilder builder)
|
protected override void Load(ContainerBuilder builder)
|
||||||
{
|
{
|
||||||
// Registrer alle handlers
|
|
||||||
builder.RegisterAssemblyTypes()
|
builder.RegisterType<PlanTempus.Components.CommandHandler>()
|
||||||
.AsClosedTypesOf(typeof(ICommandHandler<>))
|
.As<PlanTempus.Components.ICommandHandler>()
|
||||||
.InstancePerLifetimeScope();
|
.InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
builder.RegisterAssemblyTypes(ThisAssembly)
|
||||||
|
.Where(t => !typeof(ICommandHandlerDecorator).IsAssignableFrom(t))
|
||||||
|
.AsClosedTypesOf(typeof(ICommandHandler<,>))
|
||||||
|
.InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
|
||||||
|
// Registrer alle handlers
|
||||||
|
// builder.RegisterAssemblyTypes(ThisAssembly)
|
||||||
|
// .AsClosedTypesOf(typeof(ICommandHandler<,>))
|
||||||
|
// .InstancePerLifetimeScope();
|
||||||
|
|
||||||
|
builder.RegisterAssemblyTypes(ThisAssembly)
|
||||||
|
.As<ICommandHandlerDecorator>();
|
||||||
|
|
||||||
|
|
||||||
|
builder.RegisterDecorator(
|
||||||
|
typeof(CreateUserHandlerDecorator),
|
||||||
|
typeof(ICommandHandler<CreateUserCommand, CreateUserResult>));
|
||||||
|
//
|
||||||
// Registrer en decorator for alle ICommandHandler<TCommand>
|
// Registrer en decorator for alle ICommandHandler<TCommand>
|
||||||
builder.RegisterGenericDecorator(
|
// builder.RegisterGenericDecorator(
|
||||||
typeof(CreateUserHandlerDecorator<>), // Din decorator-klasse
|
// typeof(CommandHandlerDecorator<,>),
|
||||||
typeof(ICommandHandler<>)); // Interface, der skal dekoreres
|
// typeof(ICommandHandler<,>));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,6 @@ using PlanTempus.Core.Telemetry;
|
||||||
|
|
||||||
namespace PlanTempus.Components.Users.Create
|
namespace PlanTempus.Components.Users.Create
|
||||||
{
|
{
|
||||||
public interface ICommandHandler
|
|
||||||
{
|
|
||||||
Task<TCommandResult> Handle<TCommand, TCommandResult>(TCommand command );
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ICommandHandler<in T, TResult>
|
|
||||||
{
|
|
||||||
Task<TResult> Handle(T input);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CreateUserHandler(
|
public class CreateUserHandler(
|
||||||
TelemetryClient telemetryClient,
|
TelemetryClient telemetryClient,
|
||||||
IDatabaseOperations databaseOperations,
|
IDatabaseOperations databaseOperations,
|
||||||
|
|
@ -50,7 +40,6 @@ namespace PlanTempus.Components.Users.Create
|
||||||
|
|
||||||
var result = data.First();
|
var result = data.First();
|
||||||
|
|
||||||
telemetryClient.TrackTrace(GetType().Name, result.Format());
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,41 @@
|
||||||
using Microsoft.ApplicationInsights;
|
using System.Diagnostics;
|
||||||
|
using Microsoft.ApplicationInsights;
|
||||||
|
using Microsoft.ApplicationInsights.DataContracts;
|
||||||
|
|
||||||
namespace PlanTempus.Components.Users.Create;
|
namespace PlanTempus.Components.Users.Create;
|
||||||
|
|
||||||
public class CreateUserHandlerDecorator : ICommandHandler<CreateUserCommand, CreateUserResult>
|
public class CreateUserHandlerDecorator(
|
||||||
{
|
|
||||||
private readonly ICommandHandler<CreateUserCommand, CreateUserResult> _decoratedHandler;
|
|
||||||
private readonly TelemetryClient _telemetryClient;
|
|
||||||
|
|
||||||
public CreateUserHandlerDecorator(
|
|
||||||
ICommandHandler<CreateUserCommand, CreateUserResult> decoratedHandler,
|
ICommandHandler<CreateUserCommand, CreateUserResult> decoratedHandler,
|
||||||
TelemetryClient telemetryClient)
|
TelemetryClient telemetryClient)
|
||||||
|
: ICommandHandler<CreateUserCommand, CreateUserResult>, ICommandHandlerDecorator
|
||||||
{
|
{
|
||||||
_decoratedHandler = decoratedHandler;
|
|
||||||
_telemetryClient = telemetryClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<CreateUserResult> Handle(CreateUserCommand command)
|
public async Task<CreateUserResult> Handle(CreateUserCommand command)
|
||||||
{
|
{
|
||||||
_telemetryClient.TrackTrace($"Before handling {nameof(CreateUserCommand)}");
|
// var correlationId = Activity.Current?.RootId ?? command.CorrelationId;
|
||||||
var result = await _decoratedHandler.Handle(command);
|
|
||||||
_telemetryClient.TrackTrace($"After handling {nameof(CreateUserCommand)}");
|
using (var operation =
|
||||||
|
telemetryClient.StartOperation<RequestTelemetry>($"Handle {nameof(CreateUserCommand)}",
|
||||||
|
command.CorrelationId.ToString()))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
operation.Telemetry.Properties["CorrelationId"] = command.CorrelationId.ToString();
|
||||||
|
var result = await decoratedHandler.Handle(command);
|
||||||
|
|
||||||
|
operation.Telemetry.Success = true;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
operation.Telemetry.Success = false;
|
||||||
|
|
||||||
|
telemetryClient.TrackException(ex, new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["CommandType"] = nameof(CreateUserCommand)
|
||||||
|
});
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ using PlanTempus.Components;
|
||||||
using PlanTempus.Components.Users.Create;
|
using PlanTempus.Components.Users.Create;
|
||||||
using PlanTempus.Core.Database;
|
using PlanTempus.Core.Database;
|
||||||
using PlanTempus.Core.Database.ConnectionFactory;
|
using PlanTempus.Core.Database.ConnectionFactory;
|
||||||
|
using Shouldly;
|
||||||
|
|
||||||
namespace PlanTempus.X.TDD.CommandQueryHandlerTests;
|
namespace PlanTempus.X.TDD.CommandQueryHandlerTests;
|
||||||
|
|
||||||
|
|
@ -16,18 +17,21 @@ public class HandlerTest : TestFixture
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void TestDefaultConnection()
|
public async Task ShouldResolveCommandHandlerAndDispatchToGenericCommandHandler()
|
||||||
{
|
{
|
||||||
var commandHandler = Container.Resolve<CommandHandler>();
|
var commandHandler = Container.Resolve<ICommandHandler>();
|
||||||
|
commandHandler.ShouldBeOfType<CommandHandler>();
|
||||||
|
|
||||||
var command = new CreateUserCommand
|
var command = new CreateUserCommand
|
||||||
{
|
{
|
||||||
Email = "lloyd@dumbanddumber.com", // Lloyd Christmas
|
Email = "lloyd@dumbanddumber.com1", // Lloyd Christmas
|
||||||
Password = "1234AceVentura#LOL", // Ace Ventura
|
Password = "1234AceVentura#LOL", // Ace Ventura
|
||||||
IsActive = true,
|
IsActive = true,
|
||||||
CorrelationId = Guid.NewGuid()
|
CorrelationId = Guid.NewGuid()
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = commandHandler.Handle<CreateUserCommand, CreateUserResult>(command);
|
var result = await commandHandler.Handle<CreateUserCommand, CreateUserResult>(command);
|
||||||
|
|
||||||
|
result.ShouldNotBeNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
using Autofac;
|
using Autofac;
|
||||||
using Microsoft.ApplicationInsights;
|
using Microsoft.ApplicationInsights;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using PlanTempus.Components.ModuleRegistry;
|
||||||
using PlanTempus.Core.Configurations;
|
using PlanTempus.Core.Configurations;
|
||||||
using PlanTempus.Core.Configurations.JsonConfigProvider;
|
using PlanTempus.Core.Configurations.JsonConfigProvider;
|
||||||
using PlanTempus.Core.ModuleRegistry;
|
using PlanTempus.Core.ModuleRegistry;
|
||||||
|
|
@ -67,6 +68,8 @@ public abstract class TestFixture
|
||||||
SeqConfiguration = configuration.GetSection("SeqConfiguration").ToObject<SeqConfiguration>()
|
SeqConfiguration = configuration.GetSection("SeqConfiguration").ToObject<SeqConfiguration>()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
builder.RegisterModule<CommandModule>();
|
||||||
|
builder.RegisterModule<SecurityModule>();
|
||||||
|
|
||||||
ContainerBuilder = builder;
|
ContainerBuilder = builder;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue