feat: add Mapster profiles and enable DiscountOrder handlers
All checks were successful
Build and Deploy / build (push) Successful in 2m14s
All checks were successful
Build and Deploy / build (push) Successful in 2m14s
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
namespace BackOffice.BFF.WebApi.Common.Services;
|
||||
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public interface IDispatchRequestToCQRS
|
||||
{
|
||||
Task<TResponse> Handle<TRequest, TCommand, TResponse>(TRequest request,
|
||||
@@ -10,10 +14,12 @@ public interface IDispatchRequestToCQRS
|
||||
public class DispatchRequestToCQRS : IDispatchRequestToCQRS
|
||||
{
|
||||
private readonly ISender _sender;
|
||||
private readonly ILogger<DispatchRequestToCQRS> _logger;
|
||||
|
||||
public DispatchRequestToCQRS(ISender sender)
|
||||
public DispatchRequestToCQRS(ISender sender, ILogger<DispatchRequestToCQRS> logger)
|
||||
{
|
||||
_sender = sender;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<TResponse> Handle<TRequest, TCommand, TResponse>(TRequest request,
|
||||
@@ -34,10 +40,43 @@ public class DispatchRequestToCQRS : IDispatchRequestToCQRS
|
||||
}
|
||||
|
||||
var output = await _sender.Send(cqrsInput, context.CancellationToken);
|
||||
return (output ?? throw new InvalidOperationException()).Adapt<TResponse>();
|
||||
|
||||
if (output is null)
|
||||
{
|
||||
_logger.LogError("Command/Query {CommandType} returned null output", typeof(TCommand).Name);
|
||||
throw new InvalidOperationException($"Command/Query {typeof(TCommand).Name} returned null output");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Mapping from {SourceType} to {TargetType}", output.GetType().Name, typeof(TResponse).Name);
|
||||
|
||||
// Try to detect problematic properties before mapping
|
||||
LogSourceObjectStructure(output);
|
||||
|
||||
// Use TypeAdapter.Adapt instead of extension method to ensure MapWith() is used
|
||||
var result = TypeAdapter.Adapt(output, output.GetType(), typeof(TResponse));
|
||||
|
||||
_logger.LogInformation("Mapping successful: {SourceType} -> {TargetType}", output.GetType().Name, typeof(TResponse).Name);
|
||||
return (TResponse)result;
|
||||
}
|
||||
catch (Exception mappingEx)
|
||||
{
|
||||
_logger.LogError(mappingEx, "Mapping failed: {SourceType} -> {TargetType}. Full error: {ErrorMessage}",
|
||||
output.GetType().Name, typeof(TResponse).Name, mappingEx.ToString());
|
||||
|
||||
// Log detailed property information
|
||||
LogDetailedPropertyInfo(output, typeof(TResponse));
|
||||
|
||||
throw new InvalidOperationException(
|
||||
$"Failed to map {output.GetType().Name} to {typeof(TResponse).Name}. Check ProductsProfile configuration.",
|
||||
mappingEx);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error in DispatchRequestToCQRS.Handle<{Request},{Command},{Response}>",
|
||||
typeof(TRequest).Name, typeof(TCommand).Name, typeof(TResponse).Name);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -52,10 +91,36 @@ public class DispatchRequestToCQRS : IDispatchRequestToCQRS
|
||||
}
|
||||
|
||||
var output = await _sender.Send(cqrsInput, context.CancellationToken);
|
||||
return (output ?? throw new InvalidOperationException()).Adapt<TResponse>();
|
||||
|
||||
if (output is null)
|
||||
{
|
||||
_logger.LogError("Command/Query {CommandType} returned null output", typeof(TCommand).Name);
|
||||
throw new InvalidOperationException($"Command/Query {typeof(TCommand).Name} returned null output");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_logger.LogInformation("Mapping from {SourceType} to {TargetType}", output.GetType().Name, typeof(TResponse).Name);
|
||||
|
||||
// Use TypeAdapter.Adapt instead of extension method to ensure MapWith() is used
|
||||
var result = TypeAdapter.Adapt(output, output.GetType(), typeof(TResponse));
|
||||
|
||||
_logger.LogInformation("Mapping successful: {SourceType} -> {TargetType}", output.GetType().Name, typeof(TResponse).Name);
|
||||
return (TResponse)result;
|
||||
}
|
||||
catch (Exception mappingEx)
|
||||
{
|
||||
_logger.LogError(mappingEx, "Mapping failed: {SourceType} -> {TargetType}. Source properties: {@SourceData}",
|
||||
output.GetType().Name, typeof(TResponse).Name, output);
|
||||
throw new InvalidOperationException(
|
||||
$"Failed to map {output.GetType().Name} to {typeof(TResponse).Name}. Check mapping Profile configuration.",
|
||||
mappingEx);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error in DispatchRequestToCQRS.Handle<{Command},{Response}>",
|
||||
typeof(TCommand).Name, typeof(TResponse).Name);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -84,4 +149,84 @@ public class DispatchRequestToCQRS : IDispatchRequestToCQRS
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void LogSourceObjectStructure(object source)
|
||||
{
|
||||
try
|
||||
{
|
||||
var properties = source.GetType().GetProperties();
|
||||
_logger.LogInformation("Source object has {PropertyCount} properties:", properties.Length);
|
||||
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
var value = prop.GetValue(source);
|
||||
var valueType = value?.GetType().Name ?? "null";
|
||||
var isCollection = value != null && value.GetType().IsGenericType &&
|
||||
value.GetType().GetGenericTypeDefinition() == typeof(List<>);
|
||||
|
||||
if (isCollection)
|
||||
{
|
||||
var count = ((System.Collections.ICollection)value).Count;
|
||||
_logger.LogInformation(" - {PropertyName}: {PropertyType} (Collection with {Count} items)",
|
||||
prop.Name, prop.PropertyType.Name, count);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation(" - {PropertyName}: {PropertyType} = {Value}",
|
||||
prop.Name, prop.PropertyType.Name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Could not log source object structure");
|
||||
}
|
||||
}
|
||||
|
||||
private void LogDetailedPropertyInfo(object source, System.Type targetType)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sourceProps = source.GetType().GetProperties();
|
||||
var targetProps = targetType.GetProperties();
|
||||
|
||||
_logger.LogError("=== DETAILED MAPPING ANALYSIS ===");
|
||||
_logger.LogError("Source type: {SourceType} ({SourcePropsCount} properties)",
|
||||
source.GetType().FullName, sourceProps.Length);
|
||||
_logger.LogError("Target type: {TargetType} ({TargetPropsCount} properties)",
|
||||
targetType.FullName, targetProps.Length);
|
||||
|
||||
foreach (var sourceProp in sourceProps)
|
||||
{
|
||||
var targetProp = targetProps.FirstOrDefault(p =>
|
||||
p.Name.Equals(sourceProp.Name, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (targetProp != null)
|
||||
{
|
||||
var sourceValue = sourceProp.GetValue(source);
|
||||
var sourceType = sourceProp.PropertyType;
|
||||
var targetPropType = targetProp.PropertyType;
|
||||
|
||||
var isImmutable = targetPropType.FullName?.Contains("Google.Protobuf") == true ||
|
||||
targetPropType.Name.Contains("RepeatedField");
|
||||
|
||||
_logger.LogError(" Property '{PropertyName}':", sourceProp.Name);
|
||||
_logger.LogError(" Source: {SourceType} (value: {Value})",
|
||||
sourceType.Name, sourceValue);
|
||||
_logger.LogError(" Target: {TargetType} {ImmutableWarning}",
|
||||
targetPropType.Name,
|
||||
isImmutable ? "⚠️ IMMUTABLE - needs MapWith()" : "");
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning(" Property '{PropertyName}' exists in source but NOT in target",
|
||||
sourceProp.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Could not log detailed property info");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user