【问题标题】:Decorating a generic interface with Structuremap使用 Structuremap 装饰通用接口
【发布时间】:2011-11-16 04:18:46
【问题描述】:

我有一个通用接口,它接受两种通用类型。我想装饰所有返回的版本,但是由于调用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 的东西。我可以为请求和响应类型的所有组合创建特定类型的版本,但显然这是不可取的。那我错过了什么?

【问题讨论】:

  • 能够弄清楚,使用RegistrationConvention直接丰富每个封闭类型的接口。我会发布我所做的,但我不能再等几个小时。

标签: c# .net structuremap


【解决方案1】:

最终能够弄清楚。我创建了一个 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>()));
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    这是一种仍然利用 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 方法。没有它,这个问题就没有一个很好的解决方案。

    【讨论】:

      猜你喜欢
      • 2016-07-26
      • 1970-01-01
      • 1970-01-01
      • 2017-03-22
      • 1970-01-01
      • 1970-01-01
      • 2019-12-07
      • 1970-01-01
      • 2011-08-25
      相关资源
      最近更新 更多