jon*_*jon 4 c# asp.net dependency-injection castle-windsor inversion-of-control
我决定清理这篇文章,并在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/来实现"Basic"使用消息处理程序在ASP.NET Web API中进行HTTP身份验证".我是IoC/DI的新手,我正试图让它与Castle Windsor合作.
我一直在尝试很多不同的东西但是我得到以下错误之一取决于我做错了什么:
以下是我的代码:
的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;
}
Run Code Online (Sandbox Code Playgroud)
Windsor安装人员:
public class PrincipalsInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOn<DelegatingHandler>());
container.Register(
Component.For<IProvidePrincipal>().ImplementedBy<DummyPrincipalProvider>()
);
}
}
Run Code Online (Sandbox Code Playgroud)
修改后的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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
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()
{
}
}
Run Code Online (Sandbox Code Playgroud)
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();
}
}
Run Code Online (Sandbox Code Playgroud)
我假设IProvidePrincipal是您自己的实现.最好的方法,唯一一个使用IoC容器的IMHO是Composition Root.ploeh的博客很好地解释了web api的入口点/组合根.DelegatingHandler不是"请求解析"的一部分,因此您可以选择在容器作为私有变量存在的全局asax Application_start中解析它.
GlobalConfiguration.Configuration.MessageHandlers.Add(container.Resolve<BasicAuthMessageHandler>());
Run Code Online (Sandbox Code Playgroud)
如果您在容器中正确注册了处理程序及其所有依赖项,则无需执行任何其他操作:从容器中提取并在MessageHandlers中添加的处理程序实例将具有IPrincipalProvider和(I)UserRepository.请记住,BasicAuthMessageHandler将扮演一个单例,因此如果您需要在每个方法调用上使用(I)UserRepository的新实例...您可以考虑使用TypedFactory创建您的(I)UserRepository作为后期依赖项
当然,从您的顶部图形组件开始的任何组件都必须在容器中注册.
这是一种简单的方法......如果你需要更加复杂的东西,你最终可能会为DelegatingHandlers创建你的"组合根".
顺便说一下:永远不要像UserRepository userRepo = new UserRepository()这样做.或PrincipalProvider = new DummyPrincipalProvider()
没有任何"行为实例"应该明确创建:容器负责在正确的时间提供正确的实例...
根据Jon Edit: 现在DummyPrincipalProvider看起来很好:请记住,因为DummyPrincipalProvider是在消息处理程序中创建的(由于全局注册而充当单例),所以你总是重用相同的实例.
而不是你的管道
var dependencyResolver = new WindsorDependencyResolver(_container);
configuration.DependencyResolver = dependencyResolver;
Run Code Online (Sandbox Code Playgroud)
我宁愿使用ploeh实现(见上文).
你的注册
container.Register(
Component.For<IProvidePrincipal>().ImplementedBy<DummyPrincipalProvider>()
.UsingFactoryMethod(kernel => kernel.Resolve<DummyPrincipalProvider>())
);
Run Code Online (Sandbox Code Playgroud)
应该换成
container.Register(
Component.For<IProvidePrincipal>().ImplementedBy<DummyPrincipalProvider>()
);
Run Code Online (Sandbox Code Playgroud)
那是错的......容器必须解决它,而不是你明确地解决它
GlobalConfiguration.Configuration.MessageHandlers.Add(new BasicAuthMessageHandler());
Run Code Online (Sandbox Code Playgroud)
坚持我的配置如上:BasicAuthMessageHandler通过容器解决.
如果有效,请告诉我.
PS:您在容器中注册了TypedFactory工具,但您没有使用它......只是为了让您知道.您注册了DelegatingHandler(s)作为Transient,但请记住它们将被设计为"singleton":将它添加到MessageHandlers集合意味着它们将在每个请求上重用.
根据Jon Edit 2:
我在github上添加了一个示例.您应该能够构建它并使用NuGet Package Restore运行它
关于PerWebRequest的问题是关于NHibernate工厂会话创建会话"PerWebRequest"的UserRepository的依赖关系:由于HttpContext,您无法在Application_Start中解析IPrincipalProvider-> IUserRepository-> ISession.如果你真的需要一个IUserRepositry工作w/IPrincipalProvider依赖必须是IUserRepositoryFactory(TypedFactory).我尝试使用打字的工厂来修复你的样本并且它有效,但是我遇到了NHibernate配置的问题,因为我不是那个专家,所以我没有再进一步了.
如果您需要帮助w /工厂的东西...... LMK和我将使用DummyPrincipalProvider中的工厂更新我的git示例