2

我正在使用 MVC4 Web API 和 IoC 容器(在这种情况下为简单注入器,但我认为这个问题与该容器无关)构建一个 Web API,它应该公开各种 CRUD 和查询操作。在我的案例中使用 IOC 的原因是我们是一家开发商店,我需要能够让客户构建他们自己的 Web API 控制器来公开他们需要从我们的系统中公开需求的数据。因此,我希望设计我的解决方案,使我能够通过 IOC 将所有控制器(包括我们的和我们的客户的)都放在外部并可以加载,从而使我能够对自己的产品进行测试。

该网站没有对库的任何引用,但该库包含我想在网站中使用的控制器。这些类型在容器中注册,并且 DependencyResolver 设置为自定义依赖解析器。我有找到 dll 插件并加载控制器类型的代码,但是当我尝试导航到它所代表的路线时,它说它找不到它。

即如果我尝试导航到 /api/Test1Api 我应该看到文本“hello world”

我的问题是,虽然我已经加载了控制器类型,但我无法将其转换为网站所说的路径。

我得到错误

未找到与请求 URI 匹配的 HTTP 资源

找不到与名为“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);
        }
    }
}

任何帮助表示赞赏。

4

2 回答 2

4

关于您的解决方案的一个重大警告。将控制器注册到容器中也非常重要(此建议适用于所有 DI 框架,尽管某些框架默认情况下也会强制您注册具体类型)。否则你肯定会被这个开发者遇到的同样的问题所困扰。

由于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();        
    }
}
于 2013-06-01T09:52:31.217 回答
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;
    }
}

我还在 Global.asax.cs 中 App_Start 的开头添加了以下行

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

希望这对某人有帮助!

非常非常感谢史蒂文的帮助和洞察力!对此,我真的非常感激!!

于 2013-06-01T01:47:22.310 回答