ASP中的依赖注入(DI)/控制反转(IoC). NET Web API

本文关键字:IoC NET API Web 控制 注入 依赖 DI ASP | 更新日期: 2023-09-27 18:07:01

我决定清理这篇文章,我在ge.tt/3EwoZEd/v/0?c

已经花了大约30个小时在这个上面了,仍然不能弄清楚…帮助将非常感激!

我有ASP。. NET Web API解决方案,使用以下代码:http://www.piotrwalat.net/basic-http-authentication-in-asp-net-web-api-using-message-handlers/在ASP. NET中实现"基本HTTP身份验证"。. NET Web API使用消息处理程序"。我是IoC/DI的新手,我正试图让它与温莎城堡一起工作。

我一直在尝试很多不同的事情,但我得到以下错误的1取决于我做错了什么:

  • "好像你忘了注册http模块Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule"
  • BasicAuthMessageHandler中的PrincipalProvider的"对象引用未设置为对象的实例"
  • "没有支持该服务的组件*。"DummyPrincipalProvider被发现"
下面是我的代码:

Global.asax.cs:

private static IWindsorContainer _container;
protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
    var config = (CustomErrorsSection)ConfigurationManager.GetSection("system.web/customErrors");
    IncludeErrorDetailPolicy errorDetailPolicy;
    switch (config.Mode)
    {
        case CustomErrorsMode.RemoteOnly:
            errorDetailPolicy
                = IncludeErrorDetailPolicy.LocalOnly;
            break;
        case CustomErrorsMode.On:
            errorDetailPolicy
                = IncludeErrorDetailPolicy.Never;
            break;
        case CustomErrorsMode.Off:
            errorDetailPolicy
                = IncludeErrorDetailPolicy.Always;
            break;
        default:
            throw new ArgumentOutOfRangeException();
    }
    GlobalConfiguration.Configuration.IncludeErrorDetailPolicy = errorDetailPolicy;
    ConfigureWindsor(GlobalConfiguration.Configuration);
    GlobalConfiguration.Configuration.MessageHandlers.Add(new BasicAuthMessageHandler()
    {
        PrincipalProvider = _container.Resolve<IProvidePrincipal>()
    });
}
public static void ConfigureWindsor(HttpConfiguration configuration)
{
    // Create / Initialize the container  
    _container = new WindsorContainer();
    // Find our IWindsorInstallers from this Assembly and optionally from our DI assembly which is in abother project.  
    _container.Install(FromAssembly.This());
    _container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
    //Documentation http://docs.castleproject.org/Windsor.Typed-Factory-Facility.ashx  
    // Set the WebAPI DependencyResolver to our new WindsorDependencyResolver  
    var dependencyResolver = new WindsorDependencyResolver(_container);
    configuration.DependencyResolver = dependencyResolver;
}

温莎安装程序:

public class PrincipalsInstaller : IWindsorInstaller
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Classes.FromThisAssembly().BasedOn<DelegatingHandler>());
        container.Register(
            Component.For<IProvidePrincipal>().ImplementedBy<DummyPrincipalProvider>()
        );
    }
}

修改的DummyPrincipalProvider(来自我从上面的URL获得的原始):

public class DummyPrincipalProvider : IProvidePrincipal
{
    private IUserRepository _userRepo;
    public DummyPrincipalProvider(IUserRepository userRepo)
    {
        this._userRepo = userRepo;
    }
    public IPrincipal CreatePrincipal(string username, string password)
    {
        try
        {
            if (!this._userRepo.ValidateUser(username, password))
            {
                return null;
            }
            else
            {
                var identity = new GenericIdentity(username);
                IPrincipal principal = new GenericPrincipal(identity, new[] { "User" });
                if (!identity.IsAuthenticated)
                {
                    throw new ApplicationException("Unauthorized");
                }
                return principal;
            }
        }
        catch
        {
            return null;
        }
    }
}

WindsorDependencyResolver.cs:

internal sealed class WindsorDependencyResolver : IDependencyResolver
{
    private readonly IWindsorContainer _container;
    public WindsorDependencyResolver(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        _container = container;
    }
    public object GetService(Type t)
    {
        return _container.Kernel.HasComponent(t) ? _container.Resolve(t) : null;
    }
    public IEnumerable<object> GetServices(Type t)
    {
        return _container.ResolveAll(t).Cast<object>().ToArray();
    }
    public IDependencyScope BeginScope()
    {
        return new WindsorDependencyScope(_container);
    }
    public void Dispose()
    {
    }
}

WindsorDependencyScope.cs:

internal sealed class WindsorDependencyScope : IDependencyScope
{
    private readonly IWindsorContainer _container;
    private readonly IDisposable _scope;
    public WindsorDependencyScope(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        _container = container;
        _scope = container.BeginScope();
    }
    public object GetService(Type t)
    {
        return _container.Kernel.HasComponent(t) ? _container.Resolve(t) : null;
    }
    public IEnumerable<object> GetServices(Type t)
    {
        return _container.ResolveAll(t).Cast<object>().ToArray();
    }
    public void Dispose()
    {
        _scope.Dispose();
    }
}

ASP中的依赖注入(DI)/控制反转(IoC). NET Web API

我假设IProvidePrincipal是您自己的实现。使用IoC容器的最佳方法(依我之见也是唯一的方法)是组合根。ploeh的博客已经很好地解释了web api的入口点/组合根。DelegatingHandler不是"请求解析"的一部分,所以你可以选择在全局asax Application_start中解析它,其中容器作为私有变量存在。

GlobalConfiguration.Configuration.MessageHandlers.Add(container.Resolve<BasicAuthMessageHandler>());

如果你在容器中正确地注册了你的处理程序和它的所有依赖关系,没有别的事情要做:你从容器中提取的处理程序实例将有一个IPrincipalProvider和(I)UserRepository。记住BasicAuthMessageHandler将作为一个单例,所以如果你需要一个新的(I)UserRepository实例在每个方法调用…你可以考虑TypedFactory创建你的(I)UserRepository作为后期依赖

当然,任何从顶部图形组件开始的组件都必须在容器中注册。

这是最简单的方法…如果你需要更复杂的东西,你可能最终也会为DelegatingHandlers创建你的"组合根"。

顺便说一句:永远,永远,做一些像UserRepository = new UserRepository();or PrincipalProvider = new DummyPrincipalProvider()

不应该显式地创建任何"行为实例":容器负责在正确的时间提供正确的实例…

As per Jon Edit:现在DummyPrincipalProvider看起来很好:只要记住,因为DummyPrincipalProvider是在消息处理程序中创建的(由于全局注册而充当单例),您总是重用相同的实例。

而不是你的管道

var dependencyResolver = new WindsorDependencyResolver(_container);
configuration.DependencyResolver = dependencyResolver;

我宁愿使用ploeh实现(见上文)。

你的登记

container.Register(
    Component.For<IProvidePrincipal>().ImplementedBy<DummyPrincipalProvider>()
    .UsingFactoryMethod(kernel => kernel.Resolve<DummyPrincipalProvider>())
);

应替换为

container.Register(
    Component.For<IProvidePrincipal>().ImplementedBy<DummyPrincipalProvider>()
);

这是错的…容器必须解析它,而不是你显式地

GlobalConfiguration.Configuration.MessageHandlers.Add(new BasicAuthMessageHandler());

坚持我上面的配置:BasicAuthMessageHandler通过容器解析。

让我知道它是否有效。

PS:您在容器中注册了TypedFactory设施,但您没有使用它…只是想让你知道。您将DelegatingHandler注册为Transient,但请记住它们将在设计上是"单例"的:将其添加到MessageHandlers集合中意味着它们将在每个请求上被重用。

根据Jon编辑2:

我在github上添加了一个示例。您应该能够使用NuGet Package Restore构建并运行它

关于PerWebRequest的问题取决于UserRepository对NHibernate工厂会话创建会话"PerWebRequest"的依赖:由于HttpContext,您无法在Application_Start中解决IPrincipalProvider->IUserRepository->ISession。如果你真的需要一个iuserrepositstry工作/IPrincipalProvider依赖必须是IUserRepositoryFactory(TypedFactory)代替。我试图使用类型工厂修复你的样本,它工作,但我有一个问题w/NHibernate配置,因为我不是这方面的专家,我没有走得更远。

如果你需要工厂方面的帮助…LMK和我将使用DummyPrincipalProvider

中的工厂来更新我的git示例