2016-11-08 29 views
0

私はSimpleInjector v3.2.7を使用しています。ウェブAPIを構築しようとしたときにエラーが発生するHeartbeatController。そのコンストラクタは次のようになります。タイプがGetCurrentRegistrationsにリストされていますが、登録が登録されていませんというメッセージが表示されません。

public HeartbeatController(IHeartbeatService heartbeatService, IMainDbVitalCheck mainDbVitalCheck, ICacheVitalCheck cacheVitalCheck) 

container.Verify()前)container.GetCurrentRegistrations()を呼び出して、私はHeartbeatServiceにマッピングされているIHeartbeatServiceの登録を参照してください - これは私が欲しいのマッピングです。

しかしVerify()への呼び出しは次のエラーで失敗します。ここでは

The configuration is invalid. Creating the instance for type IHttpController failed. The constructor of type HeartbeatController contains the parameter with name 'heartbeatService' and type IHeartbeatService that is not registered. Please ensure IHeartbeatService is registered, or change the constructor of HeartbeatController.

は私のコンテナの設定です:

Global.asax.cs

GlobalConfiguration.Configure(WebApiConfig.Register); 

WebApiConfig.cs

public static void Register(HttpConfiguration config) 
{ 
    SimpleInjectorWebCommon.SetUpWebApi(config); 
    // ... 
    // Other unrelated stuff 
    // ... 
} 

SimpleInjectorWebCommon.cs

public static Container DiContainer { get; private set; } 

public static void SetUpWebApi(HttpConfiguration config) 
{ 
    InitContainer(config); 
    config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(DiContainer); 
} 

public static void InitContainer(HttpConfiguration config) 
{ 
    var container = new Container(); 
    container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle(); 
    container.Options.ConstructorResolutionBehavior = new GreediestConstructorBehavior(); 
    container.Options.AllowOverridingRegistrations = true; 

    RegisterServices(container); 
    container.RegisterWebApiControllers(config); 

    container.Verify(); 

    DiContainer = container; 
} 

private static void RegisterServices(Container c) 
{ 
    // ... other registrations 
    RegisterSingleImplementations(c); 
    // ... other registrations 
} 

// My gut tells me the problem is somewhere in here 
private static void RegisterSingleImplementations(Container c) 
{ 
    var assemblies = GetAllBinAssemblies("DSI.*.dll").ToList(); 
    foreach (var assembly in assemblies) 
    { 
     var registrations = assembly.GetExportedTypes() 
      .Where(t => !t.IsAbstract) 
      .Where(t => t.Namespace != null && t.Namespace.StartsWith("DSI.")) 
      .Where(t => GetDirectInterface(t.GetInterfaces()) != null) 
      .Select(t => new { Service = GetDirectInterface(t.GetInterfaces()), Implementation = t}); 


     foreach (var reg in registrations) 
     { 
      try 
      { 
       c.Register(reg.Service, reg.Implementation, Lifestyle.Scoped); 
      } 
      catch(Exception e) 
      { 

      } 
     } 
    } 

更新

問題は、私が登録するタイプのリストを取得するために、スキャン中にアセンブリをロードしたかにありました。私はAssembly.Loadを使用しましたが、最終的に現在のAppDomainに2回出現することになります。私はAppDomain.CurrentDomain.Loadを使用するように切り替えたので、今度はアセンブリはアプリケーションドメインに1回しか表示されません。ここでは、アセンブリスキャンの実装です:

private static IEnumerable <Assembly> GetAllBinAssemblies(string searchPattern) 
    { 
     var assemblyPath = AppDomain.CurrentDomain.BaseDirectory; 
     var privateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath; 
     if (string.IsNullOrEmpty(privateBinPath)) 
     { 
      return GetAssemblies(assemblyPath, searchPattern); 
     } 
     if (Path.IsPathRooted(privateBinPath)) 
     { 
      return GetAssemblies(privateBinPath, searchPattern); 
     } 
     return 
      privateBinPath.Split(';').SelectMany(bin => GetAssemblies(Path.Combine(assemblyPath, bin), searchPattern)); 
    } 

    private static List <Assembly> GetAssemblies(string path, string searchPattern) 
    { 
     return Directory 
      .GetFiles(path, searchPattern) 
      .Select(f => AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(f))) 
      .ToList(); 
    } 

答えて

1

あなたはHeartbeatControllerが依存IHeartbeatServiceインタフェースはに登録されていないことを保証する休むことができるように;-)それは言うのは難しいですが、シンプルなインジェクターは、一般的に間違っていないです。あなたが他を登録しながら

  1. あなたが誤って、同じ名前のコードベースに第2のインタフェースを定義した:

    は、一般的に間違ってここに行くことができますどのような二つの理由があります。

  2. 同じアセンブリが異なるIDでAppDomainに2回ロードされます(同じアセンブリが2つの場所からロードされる場合など)。実行時に2つのインターフェイスが存在します。

登録IHeartbeatServiceがちょうどcontainer.Verify()を呼び出す前にしてRegisterWebApiControllers後に次のコードを実行し、HeartbeatControllerのコンストラクタの引数と同じタイプであるかどうかを確認するには、次の

var heartbeatServiceRegistration = (
    from r in container.GetCurrentRegistrations() 
    where r.ServiceType.Name == "IHeartbeatService" 
    select r) 
    .Single(); 

var parameterType = 
    typeof(HeartbeatController).GetConstructors().Single() 
    .GetParameters().First().ParameterType; 

bool areSame = 
    object.ReferenceEquals(parameterType, heartbeatServiceRegistration.ServiceType); 

if (!areSame) throw new Exception("different types"); 

container.Verify(); 

areSameがfalseの場合、それは意味何らかの理由でIHeartbeatServiceという名前の複数のインターフェイスが実際に存在することに注意してください。

+0

ロードされたアセンブリリストを見て、タイプ名を確認した後、アセンブリが2回ロードされていないように見え、その名前で定義されたインターフェイスは1つだけです。これが '.Register ... 'メソッドの1つを誤って使用することによって引き起こされる可能性はありますか?これは 'WebApiRequestLifecycle'に関連していますか?他のアイデアはありますか? –

+0

@DanielGabriel 'RegisterSingleImplementations'の呼び出しの後に' AppDomain.CurrentDomain.GetAssemblies() 'を呼び出し、返されたリストに同じ名前の複数のアセンブリが含まれているかどうかを確認してください。 – Steven

+0

"これは、.Register ...メソッドの1つを誤って使用することによって発生する可能性はありますか?これはWebApiRequestLifecycleに関連していますか?"いいえ。 'Register'を間違って使う方法はありません。 – Steven

関連する問題