2011-10-31 6 views
5

私はジェネリックインターフェースを持っています。これには2つのジェネリックタイプがあります。返されるすべてのバージョンを飾りたいですが、EnrichWithを呼び出すときに型がわからないので、明らかにコンパイルされません。私はコンテキストで渡すEnrichWithオーバーロードを使用しようとしましたが、渡されたジェネリック型を取得してActivator.CreateInstanceを呼び出せるかもしれないと思っていましたが、デバッグや検査の際にコンテキストに役立つ情報はありません。Structuremapで汎用インターフェースをデコレートする

これまで私がこれまで持っていたことは次のとおりです。これは私の一般的なインタフェースです:

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

RegistrationConventionを使用して、インターフェイスの各閉じたタイプを直接充実させることができました。私がしたことを投稿したいが、数時間はできない。 – Robert

答えて

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

は、追加のサービスがあなたのデコレータに簡単に注入することができるように、まだのStructureMapのIoCの機能を活用したアプローチです。子コンテナではなくプライマリコンテナを使用していることを前提としているため完全ではありませんが、ほとんどのシナリオではおそらく動作します。

理想的には、StructureMapのIContextは、IContainerと同様にWithメソッドを公開するのが理想的です。それがなければ、本当にこの問題の大きな解決策はありません。

関連する問題