2016-11-08 4 views
0

Я использую SimpleInjector v3.2.7. Ошибка возникает при попытке создать веб-Api HeartbeatController. Его конструктор выглядит следующим образом:Тип указан в GetCurrentRegistrations, но Verify не работает, если тип не зарегистрирован

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

При вызове container.GetCurrentRegistrations() (до container.Verify()), я вижу регистрацию IHeartbeatService, который сопоставляется HeartbeatService - это отображение я хочу.

Но вызов 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) 
      { 

      } 
     } 
    } 

Update

Проблема заключалась в том, как я загружал сборку во время сканирования, чтобы получить список типов для регистрации. Я использовал Assembly.Load, который в итоге заставил бы сборку появиться дважды в текущем AppDomain. Я с тех пор перешел на использование AppDomain.CurrentDomain.Load, и теперь сборка появляется только один раз в домене приложения. Вот реализация сканирования сборки:

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

Это трудно сказать, но просто Injector, как правило, не неправильно ;-) так что вы можете быть уверены, что интерфейс IHeartbeatService, что HeartbeatController зависит в не зарегистрирован.

Есть две причины, как правило, то, что может пойти не так, здесь:

  1. Вы случайно определенные второй интерфейс в базе кода с тем же именем, в то время как вы зарегистрировали другой.
  2. Такая же сборка загружается дважды в AppDomain под другим идентификатором (например, когда одна и та же сборка загружается из двух мест), что приводит к тому, что во время выполнения есть 2 интерфейса.

Чтобы проверить, является ли зарегистрированный IHeartbeatService того же типа, как конструктор аргумента HeartbeatController «s, запустите следующий код непосредственно перед вызовом container.Verify() и после RegisterWebApiControllers:

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 ложно, это означает, что на самом деле есть несколько интерфейсов с именем IHeartbeatService по любой причине.

+0

Благодарим за быстрый ответ, но, посмотрев список загруженных сборок и проверив имя типа, он не выглядит так, как сборка загружается дважды, и есть только один интерфейс, определенный с этим именем.Есть ли вероятность, что это может быть вызвано неправильным использованием одного из методов «.Register ...»? Может ли это быть связано с 'WebApiRequestLifecycle'? У тебя есть другие идеи? –

+0

@ DanielGabriel После вызова функции «RegisterSingleImplementations» вызовите 'AppDomain.CurrentDomain.GetAssemblies()' и проверьте, содержит ли возвращенный список несколько сборок с одинаковым именем. – Steven

+0

«Есть ли вероятность, что это может быть вызвано неправильным использованием одного из методов .Register ... Может ли это быть связано с WebApiRequestLifecycle?» Неа. Невозможно использовать 'Register' неправильно. – Steven