我有一个通用接口,它有两种泛型类型。我想装饰所有返回的版本,但由于我在调用EnrichWith时不知道类型,它显然不能编译。我已经尝试使用在上下文中传递的EnrichWith重载,想想也许我可以获取传入的泛型类型并调用Activator.CreateInstance,但是在调试和检查它时,上下文没有任何有用的信息。
这是我到目前为止所拥有的。这是我的通用界面:
public interface IServiceOperation<in TRequest, out TResponse> where TResponse : ServiceResult, new()
{
TResponse PerformService(TRequest validatedRequest);
}
以下是一个示例实现:
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();
}
}
这是我的装饰者,也接受另一项服务:
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);
}
最后,这是我在容器上走了多远。这显然不能编译,但EnrichWith行显示了我想要实现的目标:
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>()));
}
}
只是因为这个问题需要更多的代码,这是我的测试,我试图通过:
[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>>();
}
}
我觉得这应该是简单的,我真的错过了如何使用StructureMap的东西。我可以为Request和Response类型的所有组合创建特定于类型的版本,但显然这是不可取的。那我错过了什么?
答案 0 :(得分:4)
最终能够弄明白。我创建了一个RegistrationConvention:
public class ServiceRegistrationConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
var interfacesImplemented = type.GetInterfaces();
foreach (var interfaceImplemented in interfacesImplemented)
{
if (interfaceImplemented.IsGenericType && interfaceImplemented.GetGenericTypeDefinition() == typeof(IServiceOperation<,>))
{
var genericParameters = interfaceImplemented.GetGenericArguments();
var closedValidatorType = typeof(ValidateServiceDecorator<,>).MakeGenericType(genericParameters);
registry.For(interfaceImplemented)
.EnrichWith((context, original) => Activator.CreateInstance(closedValidatorType, original,
context.GetInstance<IValidationService>()));
}
}
}
}
答案 1 :(得分:3)
这是一种仍然利用StructureMap的IoC功能的方法,允许将额外的服务轻松注入装饰器。它并不完美,因为它假设您使用的是主容器而不是子容器,但它可能适用于大多数情况。
public class ServiceRegistrationConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
var handlerInterfaces = (from t in type.GetInterfaces()
where t.IsGenericType &&
t.GetGenericTypeDefinition() == typeof (IHandle<,>)
select t);
foreach (var handler in handlerInterfaces)
{
var decoratorType = typeof (ValidationDecorator<,>).MakeGenericType(handler.GetGenericArguments());
registry.For(handler)
.EnrichWith((ctx, orig) => ObjectFactory.With(handler, orig).GetInstance(decoratorType));
}
}
}
理想情况下,StructureMap的IContext应该像IContainer一样公开With方法。没有它,对这个问题来说并不是一个很好的解决方案。