使用 WebAPI 2 解析器明确解决依赖关系

Resolve dependency explicitly using WebAPI 2 resolver

环境: ASP.NET 网络API 2, NInject 网络API (3.3.0.0)

用例:我正在使用 OwinStartup class 设置自定义 Owin 身份验证。 class 使用 ASP.NET Identity.IUserStore 来设置用户。我的挑战是让配置的解析器查询此引用。但是,像 MVC 中那样的解析器检索在 WebAPI

中不起作用
var userStore = DependencyResolver.Current.GetService(typeof(IUserStore<ExtendedUser, string>)) as IUserStore<ExtendedUser, string>;

我的启动配置如下:-

    public partial class Startup
    {
        System.Web.Http.Dependencies.IDependencyResolver resolver;

        public void Configuration(IAppBuilder app)
        {
            HttpConfiguration httpConfig = new HttpConfiguration();

            // Due to "new" config, the underlying resolver gets empty
            this.resolver = httpConfig.DependencyResolver;

            this.ConfigureOAuthTokenGeneration(app);

            // ... other code removed

        }

        private void ConfigureOAuthTokenGeneration(IAppBuilder app)
        {
            // Fails due to resolver being empty
            var userStore = this.resolver.GetService(typeof(IUserStore<ExtendedUser, string>)) as IUserStore<ExtendedUser, string>;
            UserService.UserStore = userStore;  
            app.CreatePerOwinContext<UserService>(UserService.Create);

            // Other code removed
        }
    }

用户服务class:-

public class UserService : UserManager<ExtendedUser, string>
{
    public UserService(IUserStore<ExtendedUser, string> store)
        : base(store)
    {
    }

    //[Ninject.Inject]
    public static IUserStore<ExtendedUser, string> UserStore { get; set; }

    public static UserService Create(IdentityFactoryOptions<UserService> options, IOwinContext context)
    {
        // var userStore = DependencyResolver.Current.GetService(typeof(IUserStore<ExtendedUser, string>)) as IUserStore<ExtendedUser, string>;
        var manager = new UserService(UserStore);
....
     }
}

接口在NInject配置中配置正确。 NInject 否则设置正确,因为没有 Owin 集成,我可以解决我的服务。

kernel.Bind<Microsoft.AspNet.Identity.IUserStore<ExtendedUser, string>>().To<UserStore<ExtendedUser, string>>();

已尝试

  1. 属性 使用 "Inject" 参数注入
  2. 将请求的 UserManager 从服务中分离出来(这也需要一个需要显式初始化的构造函数

更新 1:类似情况的另一个用例:我正在尝试在 ExceptionFilter 上设置自定义记录器,如下所示:-

   public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Exception handling
            config.Filters.Add(new ExceptionFilters());
        }
    }
  public class ExceptionFilters : ExceptionFilterAttribute
    {
       public ExceptionFilters(ZLogging.ILogger logger)
        {
            this.logger = logger;
        }
        public override void OnException(HttpActionExecutedContext context)
        {
this.logger.Log(....)
        }
   }

再次,对 ExceptionFilters 构造函数的调用在 WebApiConfig 的 Register 方法中被阻止。

在 WebApiConfig 中,我们可以使用传递的 HttpConfiguration 实例获取 DependencyResolver,然后获取记录器服务。因此,这不是问题。但是,在无法访问 HttpConfiguration 的任何地方,都会面临获取解析器的问题

更新 2 在 Startup class 中使用 DI 设置如下:-

public void Configuration(IAppBuilder app)
{
    HttpConfiguration httpConfig = new HttpConfiguration();

    httpConfig.DependencyResolver = new NinjectDependencyResolver(new Ninject.StandardKernel() );
    this.resolver = httpConfig.DependencyResolver;

..... }

虽然这解决了 Owin 引用解析的问题,但它破坏了 WebAPI 中的异常过滤器(参见更新 1),因为 Global.asax 在执行周期中启动之前首先被解析: (

需要帮助来获取解析器以提供服务。可以提供任何必要的见解。

经过整整 2 天的不同代码排序方式的多次组合,终于得出了一个可以跨域工作的解决方案。发布我的解决方案,以免其他人头疼 :)

需要 NuGet 包

  • Ninject.Web.WebApi.WebHost(所有其他都作为依赖项自动安装)
  • WebActivatorEx(确保 DI 配置代码是 运行 的第一个)

Files/Classes涉及(按运行时间顺序执行)

请注意,执行顺序很重要,因为依赖项在另一个上下文需要时才可用。

  1. NInjectWebCommon.cs(有的通过WebApi.Host组件生成,有的需要手动设置)
  2. Global.asax(一些通常放在这里的代码可能会重新定位)
  3. Startup.cs(放置 Owin 代码的位置)

NInjectWebCommon(初始化 DI 框架,将其注入执行管道)。已发布完整代码以帮助那些无法通过 NInject.Web.WebAPi.WebHost Nuget 包获得它的人)

命名空间将被 WebActivator 属性修饰为

[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(NinjectWebCommon), "Start")]
[assembly: WebActivatorEx.ApplicationShutdownMethodAttribute(typeof(NinjectWebCommon), "Stop")]

标的class

public static class NinjectWebCommon
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();

    public static void Start()
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
        bootstrapper.Initialize(CreateKernel);
    }
    public static void Stop()
    {
        bootstrapper.ShutDown();
    }
    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();

        RegisterServices(kernel);
        return kernel;
    }
    private static void RegisterServices(IKernel kernel)
    {
        kernel.Bind<ILogger>().To<NLogLogger>().InSingletonScope();
        // .... other services as below
        kernel.Bind<IEmployeeRepository>().To<EmployeeRepository>();
    }
}

Global.asax(关于路由和 DI 的所有其他配置将从此处删除)

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        Startup.HttpConfiguration = GlobalConfiguration.Configuration;
    }
}

Startup.cs(包含 Owin + WebApi 简介)

    public partial class Startup
    {
        public static HttpConfiguration HttpConfiguration { get; set; }

        public void Configuration(IAppBuilder app)
        {
            this.ConfigureOAuthTokenGeneration(app);

            this.ConfigureOAuthTokenConsumption(app);

            this.ConfigureWebApi();

            app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

            app.UseWebApi(HttpConfiguration);

            HttpConfiguration.EnsureInitialized();

        }

        private void ConfigureOAuthTokenGeneration(IAppBuilder app)
        {
            var userStore = HttpConfiguration.DependencyResolver.GetService(typeof(IUserStore<ExtendedUser, string>)) as IUserStore<ExtendedUser, string>;
            UserService.UserStore = userStore;
            app.CreatePerOwinContext<UserService>(UserService.Create);
            app.CreatePerOwinContext<SignInService>(SignInService.Create);
...
        }
 ....  other methods removed for simplification
        private void ConfigureWebApi()
        {
            HttpConfiguration.MapHttpAttributeRoutes();

            WebApiConfig.Register(HttpConfiguration);
         }
  }

WebApiConfig

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        // Exception handling
        var logger = config.DependencyResolver.GetService(typeof(ILogger)) as ILogger;
        config.Filters.Add(new ExceptionFilters(logger));
    }
}

(更新:冻结配置) 更新启动以确保配置完成(由于多个地方正在设置)

瞧瞧 一切顺利 :) 如果这有帮助,请微笑并拥抱你的队友 :)