如何连接我的Web API Castle Windsor DI代码的各个部分?

如何连接我的Web API Castle Windsor DI代码的各个部分,以便Controller的路由选择正确的接口实现?

注意 :经过几次错误的开始/死角和部分胜利( 这里 , 这里和这里 ),我将尽快确定最多500分。 但是我只会给出一个非常好的答案 – IOW,一个足够清楚,我可以理解它并“插入”到我的项目中,这样我就可以将给定的具体类挂钩到特定的Controller。

这没什么:我有一个Web API(“MVC”)项目。 但实际上,这个服务器项目没有“V”(View),所以也许更好的首字母缩略词是MRC(Model / Repository / Controller)。

无论如何,我正在尝试使用Castle Windsor添加DI。

我试图通过构造函数接口args交换具体类的概念。 但是,如何实现此function,

我一直在摔跤是一头野兽,而且我现在非常伤痕累累,血腥的头发和泥泞的鼻孔引发。

我认为,无论如何,我需要的大部分代码 – 首先。 考虑到DI,我现在有一个“DIPlumbing”文件夹和一个“DIInstallers”文件夹。 “DIPlumbing”文件夹包含两个类:WindsorCompositionRoot.cs和WindsorControllerFactory.cs。

“DIInstallers”文件夹目前有三个文件,即ISomethingProvider.cs,SomethingProvider.cs和SomethingProviderInstaller.cs

SomethingProviderInstaller似乎是将DIInstallers中的接口/类连接到DIPlumbing文件夹中的东西的关键。

(我还修改了Global.asax.cs以替换Castle Windsor替换的默认控制器路由业务)。

但我很困惑我应该在DIInstallers文件夹中放置哪些类。 这些应该取代我的存储库(它同样有一个接口和一个为每个模型实现该接口的具体类)? IOW,我应该将我的Repository代码基本上移到DIInstallers文件夹中 – 然后摆脱IRepository和Repository单元吗?

当然,这将导致在Controller类中进行必要的更改,这些更改现在引用了Repository类。

或者存储库和DIInstallers类是否共存? 如果是这样,控制器,安装程序和存储库之间的连接/关联是什么?

似乎我读到的DI和Castle Windsor越多,我就越困惑。 我不知道我是否过于密集,或者我是否想要使它变得更难,或者使用/呈现它的冲突风格是问题。 底线是:我被困在流沙中,需要Johnny Quest伸出一根坚固的竹竿。

最好的答案可能是,也许可能要求太多,将直观地表示所有这些组件 – 控制器,模型,存储库,安装程序,Global.asax.cs,组合根,工厂,提供商等。 ,彼此相关。

出于“完全公开”的目的,我将添加我希望是下面代码的关键元素,以显示我已经获得的内容以及它(希望如何)相互连接。

组成根:

public class WindsorCompositionRoot : IHttpControllerActivator { private readonly IWindsorContainer container; public WindsorCompositionRoot(IWindsorContainer container) { this.container = container; } public IHttpController Create( HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { var controller = (IHttpController)this.container.Resolve(controllerType); request.RegisterForDispose( new Release( () => this.container.Release(controller))); return controller; } private class Release : IDisposable { private readonly Action release; public Release(Action release) { this.release = release; } public void Dispose() { this.release(); } } } 

控制器工厂:

 public class WindsorControllerFactory : DefaultControllerFactory { private readonly IKernel kernel; public WindsorControllerFactory(IKernel kernel) { this.kernel = kernel; //According to my understanding of http://docs.castleproject.org/Windsor.Typed-Factory-Facility.ashx, I might need this: kernel.AddFacility(); } protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) { if (controllerType == null) { throw new HttpException(404, string.Format("The controller for path '{0}' could not be found.", requestContext.HttpContext.Request.Path)); } return (IController)kernel.Resolve(controllerType); } public override void ReleaseController(IController controller) { kernel.ReleaseComponent(controller); } 

//注意:下面的“Something”有望最终成为“Departments”,然后是其他类,现在代表模型及其相应的存储库和控制器

ISomethingProvider:

 public interface ISomethingProvider { // These are placeholder methods; I don't know which I will need yet... //bool Authenticate(string username, string password, bool createPersistentCookie); //void SignOut(); } 

SomethingProvider:

 public class SomethingProvider : ISomethingProvider { // TODO: Implement methods in ISomethingProvider, once they have been added } 

SomethingProviderInstaller:

 public class SomethingProviderInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register(Classes.FromThisAssembly() .BasedOn(typeof(ISomethingProvider)) .WithServiceAllInterfaces()); // From http://app-code.net/wordpress/?p=676; see also http://devlicio.us/blogs/krzysztof_kozmic/archive/2009/12/24/castle-typed-factory-facility-reborn.aspx container.AddFacility(); container.Register(Component.For().AsFactory()); } } 

控制器:

 public class DepartmentsController : ApiController { private readonly IDepartmentRepository _deptsRepository; public DepartmentsController(IDepartmentRepository deptsRepository) { if (deptsRepository == null) { throw new ArgumentNullException("deptsRepository is null"); } _deptsRepository = deptsRepository; } public int GetCountOfDepartmentRecords() { return _deptsRepository.Get(); } public IEnumerable GetBatchOfDepartmentsByStartingID(int ID, int CountToFetch) { return _deptsRepository.Get(ID, CountToFetch); } . . . } 

IRepository:

 public interface IDepartmentRepository { int Get(); IEnumerable Get(int ID, int CountToFetch); } 

库:

 public class DepartmentRepository : IDepartmentRepository { private readonly List departments = new List(); public DepartmentRepository() { using (var conn = new OleDbConnection( @"Provider=Microsoft.ACE.OLEDB.12.0;[bla]")) { using (var cmd = conn.CreateCommand()) { cmd.CommandText = "SELECT td_department_accounts.dept_no, IIF(ISNULL(t_accounts.name),'No Name provided',t_accounts.name) AS name FROM t_accounts INNER JOIN td_department_accounts ON t_accounts.account_no = td_department_accounts.account_no ORDER BY td_department_accounts.dept_no"; cmd.CommandType = CommandType.Text; conn.Open(); int i = 1; using (OleDbDataReader oleDbD8aReader = cmd.ExecuteReader()) { while (oleDbD8aReader != null && oleDbD8aReader.Read()) { int deptNum = oleDbD8aReader.GetInt16(0); string deptName = oleDbD8aReader.GetString(1); Add(new Department { Id = i, AccountId = deptNum, Name = deptName }); i++; } } } } } public int Get() { return departments.Count; } private Department Get(int ID) // called by Delete() { return departments.First(d => d.Id == ID); } public IEnumerable Get(int ID, int CountToFetch) { return departments.Where(i => i.Id > ID).Take(CountToFetch); } . . . } 

的Global.asax.cs:

 public class WebApiApplication : System.Web.HttpApplication { private static IWindsorContainer container; protected void Application_Start() { AreaRegistration.RegisterAllAreas(); GlobalConfiguration.Configure(WebApiConfig.Register); BootstrapContainer(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); } private static void BootstrapContainer() { container = new WindsorContainer().Install(FromAssembly.This()); var controllerFactory = new WindsorControllerFactory(container.Kernel); ControllerBuilder.Current.SetControllerFactory(controllerFactory); GlobalConfiguration.Configuration.Services.Replace( typeof(IHttpControllerActivator), new WindsorCompositionRoot(container)); } protected void Application_End() { container.Dispose(); } 

UPDATE

在尝试运行服务器时,它可以使用Fiddler2测试它以查看正在传递的内容,它在WindsorControllerFactory中的这一行失败:

 public WindsorControllerFactory(IKernel kernel) { this.kernel = kernel; kernel.AddFacility(); <-- throws exception here } 

…“ System.ArgumentException未被用户代码处理HResult = -2147024809消息=类型’Castle.Facilities.TypedFactory.TypedFactoryFacility’的工具已经在容器中注册。只有一个给定类型的工具可以存在于容器.Source = Castle.Windsor StackTrace:位于Castle.MicroKernel.DefaultKernel.AddFacility(IFacility工具)的Castle.MicroKernel.DefaultKernel.AddFacility(IFacility工具),位于HandheldServer.DIPlumbing.WindsorControllerFactory的Castle.MicroKernel.DefaultKernel.AddFacilityT cctor中的..ctor(IKernel内核):HandheldServer \ HandheldServer \ DIPlumbing \ WindsorControllerFactory.cs:HandheldServer的HandheldServer.WebApiApplication.BootstrapContainer()中的第28行:HandheldServer的HandheldServer \ HandheldServer \ Global.asax.cs:第69行。 WebApiApplication.Application_Start()在c:\ HandheldServer \ HandheldServer \ Global.asax.cs:第39行

更新2

回应克里斯蒂亚诺的回答:

所以你是说我应该将以下两个文件添加到我的安卓文件夹中(我已经有了一个DIInstallers文件夹)

PlatypusInstallerFactory.cs:

 public class PlatypusInstallerFactory : InstallerFactory { public override IEnumerable Select(IEnumerable installerTypes) { var windsorInfrastructureInstaller = installerTypes.FirstOrDefault(it => it == typeof(WindsorInfrastructureInstaller)); var retVal = new List(); retVal.Add(windsorInfrastructureInstaller); retVal.AddRange(installerTypes .Where(it => typeof(IWindsorInstaller).IsAssignableFrom(it) && !retVal.Contains(it) )); return retVal; } } 

WindsorInfrastructureInstaller.cs:

 public class WindsorInfrastructureInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { container.AddFacility(); } } 

在您的global.asax中,您将创建并使用安装程序工厂,如下所示

  var installerFactory = new PlatypusInstallerFactory(); container.Install(FromAssembly.This(installerFactory)); 

如果是,那对我有什么用? 以上是否自动注册我的Controller和/或Repository类?

更新3

我现在使用了很多来自[ http://blog.kerbyyoung.com/2013/01/setting-up-castle-windsor-for-aspnet.html#comment-form]的代码

我认为关键部分是:

的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); ConfigureWindsor(GlobalConfiguration.Configuration); } public static void ConfigureWindsor(HttpConfiguration configuration) { _container = new WindsorContainer(); _container.Install(FromAssembly.This()); _container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true)); var dependencyResolver = new WindsorDependencyResolver(_container); configuration.DependencyResolver = dependencyResolver; } 

WindsorDependencyResolver.cs:

 namespace HandheldServer { public class WindsorDependencyResolver : System.Web.Http.Dependencies.IDependencyResolver { private readonly IWindsorContainer _container; public WindsorDependencyResolver(IWindsorContainer container) { _container = container; } public IDependencyScope BeginScope() { return new WindsorDependencyScope(_container); } public object GetService(Type serviceType) { if (!_container.Kernel.HasComponent(serviceType)) { return null; } return this._container.Resolve(serviceType); } public IEnumerable GetServices(Type serviceType) { if (!_container.Kernel.HasComponent(serviceType)) { return new object[0]; } return _container.ResolveAll(serviceType).Cast(); } public void Dispose() { _container.Dispose(); } } public class WindsorDependencyScope : IDependencyScope { private readonly IWindsorContainer _container; private readonly IDisposable _scope; public WindsorDependencyScope(IWindsorContainer container) { this._container = container; this._scope = container.BeginScope(); } public object GetService(Type serviceType) { if (_container.Kernel.HasComponent(serviceType)) { return _container.Resolve(serviceType); } else { return null; } } public IEnumerable GetServices(Type serviceType) { return this._container.ResolveAll(serviceType).Cast(); } public void Dispose() { this._scope.Dispose(); } } public class ApiControllersInstaller : IWindsorInstaller { public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store) { container.Register(Classes.FromThisAssembly() // should it be Types instead of Classes? .BasedOn() .LifestylePerWebRequest()); } } // This idea from https://github.com/argeset/set-locale/blob/master/src/client/SetLocale.Client.Web/Configurations/IocConfig.cs public class ServiceInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register( Component.For().ImplementedBy().LifestylePerWebRequest(), Component.For().ImplementedBy().LifestylePerWebRequest(), Component.For().ImplementedBy().LifestylePerWebRequest(), Component.For().ImplementedBy().LifestylePerWebRequest(), Component.For().ImplementedBy().LifestylePerWebRequest(), Component.For().ImplementedBy().LifestylePerWebRequest(), Component.For().ImplementedBy().LifestylePerWebRequest()); } } } 

更新4

对于SO来说, 这个问题可能过于笼统,所以我把它发布在“程序员”上

更新5

注意:根据“The DI Whisperer”(Mark Seemann),不应使用IDependencyResolver,因为它缺少Release方法(他的书第207页)

不想再重复所有内容,所以请查看我的答案如何让Web API / Castle Windsor识别控制器? 。

另外注意 – 如果你可以提供帮助,我建议不要在你的存储库构造函数中做任何事情。 我之所以这样说是因为Windsor正在尝试实例化存储库的正确实例,因此构造函数被调用。 这意味着如果发生任何类型的错误,它会在WebApi尝试创建控制器时发生。 这有时可能会使追踪问题变得有点棘手,并最终隐藏了大量例外情况下的实际问题。

你不应该混合安装和解决。 我不应该

 kernel.AddFacility(); 

在WindsorControllerFactory中

但是注册TypedFactoryFacility的通用容器配置应该在尽可能早地调用的安装程序中执行。

为了驱动安装程序执行,您应该使用安装程序工厂

 public class YourInstallerFactory : InstallerFactory { public override IEnumerable Select(IEnumerable installerTypes) { var windsorInfrastructureInstaller = installerTypes.FirstOrDefault(it => it == typeof(WindsorInfrastructureInstaller)); var retVal = new List(); retVal.Add(windsorInfrastructureInstaller); retVal.AddRange(installerTypes .Where(it => typeof(IWindsorInstaller).IsAssignableFrom(it) && !retVal.Contains(it) )); return retVal; } } 

windsorInfrastructureInstaller会像这样吵架

 public class WindsorInfrastructureInstaller : IWindsorInstaller { public void Install(IWindsorContainer container, IConfigurationStore store) { // Resolvers //container.Kernel.Resolver.AddSubResolver(new ArrayResolver(container.Kernel)); // TypedFactoryFacility container.AddFacility(); } } 

在您的global.asax中,您将创建并使用安装程序工厂,如下所示

  var installerFactory = new YourInstallerFactory(); container.Install(FromAssembly.This(installerFactory)); 

你的“FrontEnd”(例如mvc / webapi)项目有一个包含所有安装程序的文件夹(WindsorInfrastructureInstaller将是其中之一)和安装程序工厂,或者至少是我用来组织我的解决方案的方式。

在回答我自己的问题时,我只想说:没有短语! 不停地走,或者更进一步, 去这里拿到这本书。 让自己花些时间仔细阅读。

所以我不是唯一一个; 这是杰夫贝克的一句话,他写了这本书的前言:“ 通常那些开始使用DI的人很快发现自己迷失在混乱的海洋中。