Tengo una interfaz genérica, que admite dos tipos genéricos. Quiero decorar todas las versiones devueltas, pero como no sé el tipo cuando llamo a EnrichWith, obviamente no compila. He intentado utilizar la sobrecarga EnrichWith que pasa en el contexto, pensando que tal vez podría tomar los tipos genéricos pasados y llamar a Activator.CreateInstance, pero el contexto no tiene información útil al depurarlo e inspeccionarlo.Decorar una interfaz genérica con Structuremap
Esto es lo que tengo hasta ahora. Este es mi interfaz genérica:
public interface IServiceOperation<in TRequest, out TResponse> where TResponse : ServiceResult, new()
{
TResponse PerformService(TRequest validatedRequest);
}
Aquí está un ejemplo de implementación:
public class SignUpService : IServiceOperation<SignUpRequest, SignUpResult>
{
private readonly IUserRepository _userRepo;
public SignUpService(IUserRepository userRepo)
{
_userRepo = userRepo;
}
public SignUpResult PerformService(SignUpRequest validatedRequest)
{
var user = Mapper.Map<User>(validatedRequest);
user.MarkAsLoggedIn();
user.ChangePassword(validatedRequest.UnhashedPassword);
using(var transaction = _userRepo.BeginTransaction())
{
_userRepo.Save(user);
transaction.Commit();
}
return new SignUpResult();
}
}
Aquí es mi decorador, que se lleva en otro servicio, así:
public class ValidateServiceDecorator<TRequest, TResponse> : IServiceOperation<TRequest, TResponse> where TResponse : ServiceResult, new()
{
private readonly IServiceOperation<TRequest, TResponse> _serviceOperation;
private readonly IValidationService _validationService;
public ValidateServiceDecorator(IServiceOperation<TRequest, TResponse> serviceOperation,
IValidationService validationService)
{
_serviceOperation = serviceOperation;
_validationService = validationService;
}
public TResponse PerformService(TRequest request)
{
var response = new TResponse();
var validationResult = _validationService.Validate(request);
if (!validationResult.IsValid)
{
response.ValidationErrors = validationResult.ValidationErrors;
return response;
}
return _serviceOperation.PerformService(request);
}
Por último, aquí es cómo Hasta ahora he subido a mi contenedor. Esto, obviamente, no se compila, pero la línea EnrichWith muestra lo que estoy tratando de lograr:
public class StructureMapServiceScanner : Registry
{
public StructureMapServiceScanner()
{
Scan(scanner =>
{
scanner.AssemblyContainingType(typeof (IServiceOperation<,>));
scanner.ConnectImplementationsToTypesClosing(typeof (IServiceOperation<,>));
});
For(typeof (IServiceOperation<,>))
.EnrichWith((ioc, original) => new ValidateServiceDecorator(original, ioc.GetInstance<IValidationService>()));
}
}
Y sólo porque esta pregunta necesitaba un poco más de código, aquí está mi prueba de que estoy tratando de llegar a pasar :
[TestClass]
public class StructureMapServiceScannerSpecs
{
[TestMethod]
public void Test()
{
ObjectFactory.Configure(cfg =>
{
cfg.AddRegistry<StructureMapServiceScanner>();
cfg.For<IUserRepository>().Use(new Mock<IUserRepository>().Object);
cfg.For<IValidationService>().Use(new Mock<IValidationService>().Object);
});
var service = ObjectFactory.GetInstance<IServiceOperation<SignUpRequest, SignUpResult>>();
service.ShouldNotBeNull();
service.ShouldBeType<ValidateServiceDecorator<SignUpRequest, SignUpResult>>();
}
}
Siento que esto es algo que debería ser simple, y realmente me falta algo con cómo usar StructureMap. Pude crear versiones de tipo específico para todas las combinaciones de tipos de Solicitud y Respuesta, pero obviamente eso no es deseable. Entonces, ¿qué me estoy perdiendo?
Pude resolverlo, usando RegistrationConvention para enriquecer cada tipo cerrado de la interfaz directamente. Publicaba lo que hice, pero no puedo por unas pocas horas. – Robert