2011-10-31 12 views
5

Mam ogólny interfejs, który przyjmuje dwa typy ogólne. Chcę ozdobić wszystkie wersje zwrócone, ale ponieważ nie znam typu podczas wywoływania EnrichWith, to oczywiście nie kompiluje. Próbowałem używać EnrichWith Przeciążenie, które przechodzi w kontekście, myśląc, że może mógłbym pobrać generycznych typów przekazanych i wywołanie Activator.CreateInstance, ale kontekst nie ma żadnych użytecznych informacji na nim podczas debugowania i kontroli.Dekorowanie ogólnego interfejsu za pomocą Structuremap

Oto, co mam do tej pory. To jest mój rodzajowy interfejs:

public interface IServiceOperation<in TRequest, out TResponse> where TResponse : ServiceResult, new() 
{ 
    TResponse PerformService(TRequest validatedRequest); 
} 

Oto realizacja próbki:

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(); 
    } 
} 

Oto mój dekorator, który odbywa się w innej usłudze, a także:

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); 
    } 

Wreszcie, tutaj jest jak daleko dostałem się na mój pojemnik. To oczywiście nie kompiluje, ale linia EnrichWith pokazuje co usiłuję osiągnąć:

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>())); 
    } 
} 

I właśnie dlatego kwestia ta potrzebuje trochę więcej kodu, oto mój test, który próbuję dostać się do przejść :

[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>>(); 
    } 
} 

Czuję, że to jest coś, co powinno być proste, a ja naprawdę brakuje mi czegoś, jak korzystać z StructureMap. Mógłbym tworzyć wersje dla konkretnych typów dla wszystkich kombinacji typów Żądania i Odpowiedzi, ale oczywiście nie jest to pożądane. Więc czego mi brakuje?

+0

Udało się to zrozumieć, korzystając z RegistrationConvention, aby bezpośrednio wzbogacić każdy zamknięty typ interfejsu. Opublikuję to, co zrobiłem, ale nie mogę przez kolejne kilka godzin. – Robert

Odpowiedz

4

Byłem w stanie to rozgryźć, w końcu. I stworzył 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>())); 
      } 
     } 
    } 
} 
3

tu jest podejście, które nadal wykorzystuje możliwości StructureMap w MKOl, umożliwiając dodatkowe usługi, aby być łatwo wstrzykiwany do swojego dekoratora. Nie jest idealny, ponieważ zakłada, że ​​używasz kontenera podstawowego, a nie kontenera podrzędnego, ale prawdopodobnie będzie działać w większości przypadków.

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)); 
     } 
    } 
} 

Idealnie, IContext StructureMap powinien wystawiać metodę With tak jak IContainer. Bez tego nie ma naprawdę świetnego rozwiązania tego problemu.

Powiązane problemy