2

我正在使用MVC4 Web API和IoC容器(本例中爲簡單注入器,但我認爲此問題與該容器無關)構建Web API應該公開各種CRUD和查詢操作。在我的情況下使用IOC的原因是我們是一個開發商店,我需要能夠讓客戶構建自己的Web API控制器來公開他們需要從我們的系統公開需要的數據。因此,我希望能夠設計我的解決方案,讓我可以通過讓所有控制器,包括我們的和我們的客戶,通過國際奧委會的外部和可加載的方式,來讓我自己的產品受到損害。無法解析從外部DLL加載的控制器

該網站沒有任何引用該庫,但該庫包含我想在網站中使用的控制器。類型在容器中註冊,並且DependencyResolver被設置爲自定義依賴關係解析器。我有代碼找到dll插件並加載控制器類型,但是當我嘗試導航到它將代表它的路線時說它無法找到它。

也就是說,如果我嘗試導航到/ API/Test1Api我應該看到文本「Hello World」

我在這裏的問題是,雖然我已加載我的控制器類型,我無法翻譯成路線網站上說的是那裏。

我得到的錯誤

沒有HTTP資源發現,請求URI

沒有類型被發現,名爲「Test1Api」控制器匹配匹配。

這裏是我註冊的容器

public static class SimpleInjectorInitializer 
{ 
    /// <summary>Initialize the container and register it as MVC3 Dependency Resolver.</summary> 
    public static void Initialize() 
    { 
     //// Did you know the container can diagnose your configuration? Go to: http://bit.ly/YE8OJj. 

     // Create the IOC container. 
     var container = new Container(); 

     InitializeContainer(container); 

     container.RegisterMvcAttributeFilterProvider(); 
     // Verify the container configuration 
     container.Verify(); 

     // Register the dependency resolver. 
     GlobalConfiguration.Configuration.DependencyResolver = 
        new SimpleInjectorWebApiDependencyResolver(container); 
    } 

    private static void InitializeContainer(Container container) 
    { 
     var appPath = AppDomain.CurrentDomain.BaseDirectory; 

     string[] files = Directory.GetFiles(appPath + "\\bin\\Plugins", "*.dll", 
      SearchOption.AllDirectories); 
     var assemblies = files.Select(Assembly.LoadFile); 

     // register Web API controllers 
     var apiControllerTypes = 
      from assembly in assemblies 
      where !assembly.IsDynamic 
      from type in assembly.GetExportedTypes() 
      where typeof(IHttpController).IsAssignableFrom(type) 
      where !type.IsAbstract 
      where !type.IsGenericTypeDefinition 
      where type.Name.EndsWith("Controller", StringComparison.Ordinal) 
      select type; 

     // register MVC controllers 
     var mvcControllerTypes = 
      from assembly in assemblies 
      where !assembly.IsDynamic 
      from type in assembly.GetExportedTypes() 
      where typeof(IController).IsAssignableFrom(type) 
      where !type.IsAbstract 
      where !type.IsGenericTypeDefinition 
      where type.Name.EndsWith("Controller", StringComparison.Ordinal) 
      select type; 

     foreach (var controllerType in apiControllerTypes) 
     { 
      container.Register(controllerType); 
     } 

     foreach (var controllerType in mvcControllerTypes) 
     { 
      container.Register(controllerType); 
     } 
    } 
} 

任何幫助表示讚賞。

+0

你在這裏做什麼是你的DI容器註冊這些控制器類型。但是Web API有自己的搜索(和緩存)控制器類型的過程。將它們註冊到容器中並不意味着Web API可以自動解決它們。 – Steven

+0

但是,如果我沒有弄錯,Web API的默認行爲是在所有加載的程序集中搜索控制器(使用'IAssembliesResolver' /'DefaultAssembliesResolver')。由於您加載了這些插件程序集,我預計Web API會自動選取它們。 – Steven

+1

爲什麼重新編寫自己的容器重新發明輪子?其他人已經經歷了重大的發展和測試,使用其中的一個不是更好嗎? – Richard

回答

3

我想通了!

因此,我在我的Web Api調用CustomAssembliesResolver中創建了一個新類,它繼承自DefaultAssembliesResolver。基本上,我將我的程序集添加到查找控制器時解析的程序集列表中。我仍然使用Simple Injector作爲解決方案的DI部分。

public class CustomAssembliesResolver : DefaultAssembliesResolver 
{ 
    public override ICollection<Assembly> GetAssemblies() 
    { 
     var appPath = AppDomain.CurrentDomain.BaseDirectory; 
     var baseAssemblies = base.GetAssemblies(); 
     var assemblies = new List<Assembly>(baseAssemblies); 
     var files = Directory.GetFiles(appPath + "\\bin\\Plugins", "*.dll", 
         SearchOption.AllDirectories); 
     var customAssemblies = files.Select(Assembly.LoadFile); 

     // register Web API controllers 
     var apiControllerAssemblies = 
      from assembly in customAssemblies 
      where !assembly.IsDynamic 
      from type in assembly.GetExportedTypes() 
      where typeof(IHttpController).IsAssignableFrom(type) 
      where !type.IsAbstract 
      where !type.IsGenericTypeDefinition 
      where type.Name.EndsWith("Controller", StringComparison.Ordinal) 
      select assembly; 

     foreach (var assembly in apiControllerAssemblies) 
     { 
      baseAssemblies.Add(assembly); 
     } 

     return assemblies; 
    } 
} 

我還添加了以下行App_Start年初在Global.asax.cs中

GlobalConfiguration.Configuration.Services.Replace(typeof(IAssembliesResolver), new CustomAssembliesResolver()); 

希望這可以幫助別人!

巨大的,非常感謝Steven的幫助和洞察!對此,我真的非常感激!!

+0

我一直在搜索整天,以弄清楚這一點。謝謝!!! – Joe

+1

只是一個側面說明,Assembly.Load將鎖定文件,但您可以使用Assembly.Load(File.ReadAllBytes(path))來避免鎖定。 –

4

有關您的解決方案的一個重大警告。把控制器註冊到你的容器中也是非常重要的(這個建議適用於所有的DI框架,儘管一些框架默認也會強制你註冊具體的類型)。否則,您肯定會被與this developer had相同的問題困擾。

由於IHttpControllerTypeResolver使用IAssembliesResolver,最簡單(也是最安全)的解決方案是簡單地要求IHttpControllerTypeResolver註冊所有控件。你SimpleInjectorInitializer在這種情況下,看起來就像這樣:

public static class SimpleInjectorInitializer 
{ 
    public static void Initialize() 
    { 
     // Create the IOC container. 
     var container = new Container(); 

     InitializeContainer(container); 

     container.RegisterMvcAttributeFilterProvider(); 

     // Verify the container configuration 
     container.Verify(); 

     // Register the dependency resolver. 
     GlobalConfiguration.Configuration.DependencyResolver = 
      new SimpleInjectorWebApiDependencyResolver(container); 
    } 

    private static void InitializeContainer(Container container) 
    { 
     GlobalConfiguration.Configuration.Services 
      .Replace(typeof(IAssembliesResolver), 
       new CustomAssembliesResolver()); 

     var services = GlobalConfiguration.Configuration.Services; 
     var controllerTypes = services.GetHttpControllerTypeResolver() 
      .GetControllerTypes(services.GetAssembliesResolver()); 

     // register Web API controllers (important!) 
     foreach (var controllerType in controllerTypes) 
     { 
      container.Register(controllerType); 
     }   
    } 
} 

另外請注意,您的CustomAssembliesResolver可以做得更簡單:

public class CustomAssembliesResolver 
    : DefaultAssembliesResolver 
{ 
    private Assembly[] plugins = (
     from file in Directory.GetFiles(
      appPath + "\\bin\\Plugins", "*.dll", 
      SearchOption.AllDirectories) 
     let assembly = Assembly.LoadFile(file) 
     select assembly) 
     .ToArray(); 

    public override ICollection<Assembly> GetAssemblies() 
    { 
     var appPath = 
      AppDomain.CurrentDomain.BaseDirectory; 

     return base.GetAssemblies() 
      .Union(this.plugins).ToArray();   
    } 
} 
+0

甜!感謝您的幫助:) – abraganza

+0

我改變了插件線以下,因爲我認爲有一個選擇失蹤。 '私人只讀大會[]插件=(從Directory.GetFiles文件( AppDomain.CurrentDomain.BaseDirectory + 「\\倉\\插件」, 「* .DLL」, SearchOption.AllDirectories) 讓組件=彙編.LoadFile(文件) select assembly).ToArray();' – abraganza

+0

@abraganza:我覺得有:) – Steven

相關問題