Как мне соединить различные части моего кода API веб-интерфейса Castle Windsor?
Как мне соединить различные части моего кода API веб-интерфейса Castle Windsor, чтобы контроллер 's маршрутизация выбирает правильную реализацию интерфейса?
Заметка: После нескольких фальстартов / тупиков и частичных побед (Вот а такжеВот а такжеВот), Я собираюсь поднять это как можно скорее для максимума500 точки. Но я'Я только собираюсь наградить действительно хороший ответ - IOW, достаточно ясный, чтобы я мог его понять, и "включи в мой проект, чтобы я мог подключить конкретный класс к конкретному контроллеру.
Здесь ничего не идет: у меня есть веб-API ("MVC») проект. Правда, у этого серверного проекта нет буквы "V" (Вид), так что, возможно, лучшим аббревиатурой будет MRC (Модель / Репозиторий / Контроллер).
Во всяком случае, яЯ пытаюсь добавить DI к нему с помощью Castle Windsor.
Я использую и копаю концепцию обмена конкретными классами через аргументы интерфейса конструктора. Как реализовать эту функциональность,
был зверем яя боролся сЯ довольно ушибленный и кровавый в настоящее время, с запутанными волосами и инкрустированными грязью ноздрями.
Думаю, у меня есть большая часть кода, который мне нужен, во всяком случае, для начала. Имея в виду 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, я должен в основном переместить мой код Репозитория в папку DIInstallers - и затем избавиться от модулей IRepository и Repository?
Это, конечно, приведет к необходимым изменениям в классах Controller, которые на данный момент ссылаются на классы репозитория.
Или классы Repositories и DIInstallers сосуществуют? Если да, то какова связь / связь между контроллерами, установщиками и репозиториями?
Кажется, чем больше я читаю о DI и Castle Windsor, тем больше я запутался. Я нене знаю, если яЯ слишком плотный для этого, или если яЯ пытаюсь сделать это сложнее, чем есть, или если проблема заключается в противоречивых стилях его использования / представления. Суть в том, что яЯ застрял в зыбучих песках и нуждаюсь в Джонни Квесте, чтобы вытянуть крепкий бамбуковый жезл.
Наилучшим ответом, возможно, и, вероятно, слишком большим, чтобы спросить, было бы визуальное представление о том, как все эти компоненты - контроллеры, модели, репозитории, установщики, 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);
}
// Обратите внимание "Что-то" ниже, надеюсь, в конце концов будетОтделы» а затем другие классы теперь представлены в моделях и их соответствующих репозиториях и контроллерах
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);
}
Repository:
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();
}
ОБНОВИТЬПри попытке запустить сервер, чтобы он мог проверить его с помощью Fiddler2, чтобы увидеть, что именно передается, он потерпел неудачу в этой строке в WindsorControllerFactory:
public WindsorControllerFactory(IKernel kernel)
{
this.kernel = kernel;
kernel.AddFacility(); 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Примечание: согласноД.И. Шепот " (Mark Seemann), IDependencyResolver не следует использовать, поскольку в нем отсутствует метод Release (стр. 207 его книги)