我已经在这个问题上挣扎了很长一段时间,所以我开始认为我已经创建了一个反模式。尽管如此,还是要开始了;
//Register self
container.Register(Component.For<IWindsorContainer>().Instance(container));
//Register all
container.Register(Component.For<IService1>().ImplementedBy<Service1>());
container.Register(Component.For<IService2>().ImplementedBy<Service2>());
//etc
IService1
{
//blabla
}
IService2 {IService1 Service1{get;}}因此,可以创建IService1和IService2而不需要任何特殊操作。从IService3开始,就涉及到IProject。
IProject{}
//Resolve a service that, amongst other things, relies on an IProject
IProjectGet
{
T Get<T>(IProject proj)
where T : class;
}
//Impl
ProjectGet : IProjectGet
{
IWindsorContainer _cont;
public ProjectGet(IWindsorContainer cont){_cont=cont}
public T Get<T>(IProject proj)
{
//Resolve using the main (and only) container and pass the IProject
return _cont.Resolve<T>(new {p = proj});
}
}这不起作用,只有主服务被解析为'p = proj‘和主服务所具有的任何其他依赖项,这些依赖项也依赖于项目,导致未找到项目服务的异常。
IService3
{
IService2 Service2{get;}
IProjectGet ProjectGet{get;}
IProjectLevelStuff SetActiveProject(IProject proj);
}
Service3 : IService3
{
IService2 Service2{get;private set;}
IProjectGet ProjectGet{get;private set;}
public Service3(IService2 s2, IProjectGet p)
{
ProjectGet = p;
Service2 = s2;
}
public IProjectLevelStuff SetActiveProject(IProject proj)
{
return ProjectGet.Get<IProjectLevelStuff>(proj);
}
}
ProjectLevelStuff : IProjectLevelStuff
{
IProject Project{get;private set;}
IService4 Service4 {get;private set;}
public ProjectLevelStuff(IProject p, IService4)//etc.
}
IService4
{
IService2 Service2{get;}
IService5 Service5{get;}
IService6 Service6{get;}
IProject Project{get;}
}
IService5{IProject Project{get;}}
IService6{IProject Project{get;}}这会失败,因为只有ProjectLevelStuff会将IProject传递给它,而且由于IService4及其依赖项也需要它,因此会抛出一个异常。即使这确实有效,我也不喜欢它,因为每个依赖于IProject的服务都被强制调用参数'p‘,这是我想要避免的。
我只想继续使用我已经拥有的服务,但这次添加了传递给我们的泛型Get方法的IProject实例作为一个可解析的依赖项。我发现没有办法复制容器并创建一个新的容器,然后将主容器添加为子容器并不会改变任何东西(依赖项仍然缺失)。这是怎么做的?
Castle Windsor确实有一个内置的TypeFactory,但它基本上和我已经在做的事情是一样的,并且不能解决任何问题。我找到的唯一的“解决方案”是创建一个新的容器并重新注册类型,但这一次通过主容器解析它们(当然,除了IProject )。这是一个工作中的维护噩梦。
更新:我在下面的答案中添加了一些单元测试,希望能澄清一些事情
发布于 2013-04-29 15:58:30
请检查您是否可以使用以下使用作用域的方法:
[SetUp]
public void Setup()
{
int counter = 0;
_container = new WindsorContainer();
_container.AddFacility<TypedFactoryFacility>();
_container.Register(
Component.For<IService1>().ImplementedBy<Service1>().LifestyleScoped(),
Component.For<IService2>().ImplementedBy<Service2>().LifestyleScoped(),
Component.For<IService3>().ImplementedBy<Service3>().LifestyleScoped(),
Component.For<Class1>().LifestyleTransient(),
Component.For<Class2>().LifestyleTransient(),
Component.For<IProject>().ImplementedBy<Project>().LifestyleScoped().DynamicParameters((k, d) => d["name"] = "MyProjectName"+counter++)
);
}
[Test]
public void TestClass1()
{
using (_container.BeginScope())
{
Class1 object1 = _container.Resolve<Class1>();;
var object2 = _container.Resolve<Class1>();
Assert.AreNotSame(object1, object2);
Assert.AreSame(object1.Service1, object2.Service1);
}
}
[Test]
public void TestClass2()
{
Class2 object1;
using (_container.BeginScope())
{
object1 = _container.Resolve<Class2>();
var object2 = _container.Resolve<Class2>();
Assert.AreNotSame(object1, object2);
Assert.AreSame(object1.Project, object2.Project);
Assert.AreSame(object1.Service2.Project, object2.Service2.Project);
}
Class2 object3;
using (_container.BeginScope())
{
object3 = _container.Resolve<Class2>();
}
Assert.AreNotSame(object1.Project, object3.Project);
}发布于 2013-04-25 21:52:45
出于某种奇怪的(但可能是有效的)原因,Windsor子容器可以访问它们的父容器,但反过来却不能。这意味着为了从新容器中使用在主容器中注册的服务,我们必须设置主容器的父容器,而不是新容器的父容器。
这是非常不方便的,因为一个容器只能有一个父级。
internal class ProjServices : IProjServices
{
private readonly IKwProject _proj;
private readonly IWindsorContainer _mainCont;
public ProjServices(IKwProject proj, IWindsorContainer mainCont)
{
_mainCont = mainCont;
_proj = proj;
}
public T Resolve<T>()
{
T rett;
//Create new container
var projCont = new WindsorContainer();
//Register new service
projCont.Register(Component.For<IKwProject>().Instance(_proj));
//Set hierarchy
lock (_mainCont)
{
projCont.AddChildContainer(UiContainer); //ui needs project, set parent to projCont
UiContainer.AddChildContainer(_mainCont); //main needs ui, set parent to uiCont
//Resolve using main, which now has access to UI and Project services
try
{
rett = _mainCont.Resolve<T>();
}
finally
{
projCont.RemoveChildContainer(UiContainer);
UiContainer.RemoveChildContainer(_mainCont);
}
}
return rett;
}
private static readonly object UIContainerLock = new object();
private static volatile IWindsorContainer _uiContainer;
private static IWindsorContainer UiContainer
{
get
{
if(_uiContainer==null)
lock(UIContainerLock)
if (_uiContainer == null)
{
//Register the UI services
}
return _uiContainer;
}
}
}现在,如果我将来想在更新的容器中使用这些新容器,我想我会因为单亲-唯一的事情而再次被卡住……我该如何正确地做到这一点?
更新:
VS 2010和2012的单元测试:
ServiceTest.zip (798 KB) https://mega.co.nz/#!z4JxUDoI!UEnt3TCoMFVg-vXKEAaJrhzjxfhcvirsW2hv1XBnZCc
或复制和粘贴:
using System;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ServiceTest
{
/// <summary>
/// A service that doesn't rely on anything else
/// </summary>
public interface IService1
{
}
class Service1 : IService1
{
}
/// <summary>
/// The Project
/// </summary>
public interface IProject
{
string Name { get; }
}
public class Project : IProject
{
public Project(string name)
{
Name = name;
}
public string Name { get; private set; }
}
/// <summary>
/// A Service that relies on a Project
/// </summary>
public interface IService2
{
IProject Project { get; }
string GetProjectName();
}
/// <summary>
/// The implementation shows it also relies on IService3
/// </summary>
public class Service2 : IService2
{
public Service2(IProject project, IService3 service3)
{
Project = project;
Service3 = service3;
}
public IProject Project { get; private set; }
public IService3 Service3 { get; private set; }
public string GetProjectName()
{
return Project.Name;
}
}
/// <summary>
/// IService3 is a Service that also relies on the Project
/// </summary>
public interface IService3
{
IProject Project { get; }
}
public class Service3 : IService3
{
public Service3(IProject project)
{
Project = project;
}
public IProject Project { get; private set; }
}
/// <summary>
/// Class1 uses the service without any dependencies so it will be easy to resolve
/// </summary>
public class Class1
{
public Class1(IService1 service1)
{
Service1 = service1;
}
public IService1 Service1 { get; private set; }
}
/// <summary>
/// Class2 also uses that service, but it also relies on a Project ánd IService2
/// which as you know also relies on the Project and IService3 which also relies on
/// the Project
/// </summary>
public class Class2
{
public Class2(IService1 service1, IProject project, IService2 service2)
{
Service1 = service1;
Project = project;
Service2 = service2;
}
public IProject Project { get; private set; }
public IService1 Service1 { get; private set; }
public IService2 Service2 { get; private set; }
}
/// <summary>
/// Set up the base services
/// </summary>
[TestClass]
public class UnitTestBase
{
protected WindsorContainer Cont;
[TestInitialize]
public void BaseSetup()
{
Cont = new WindsorContainer();
Cont.Register(Component.For<IService1>().ImplementedBy<Service1>().LifestyleTransient());
Cont.Register(Component.For<IService2>().ImplementedBy<Service2>().LifestyleTransient());
Cont.Register(Component.For<IService3>().ImplementedBy<Service3>().LifestyleTransient());
Cont.Register(Component.For<Class1>().LifestyleTransient());
Cont.Register(Component.For<Class2>().LifestyleTransient());
}
[TestMethod]
public void Class1_Resolves()
{
Cont.Resolve<Class1>();
}
}
/// <summary>
/// Set up the base unit tests
/// </summary>
[TestClass]
public class UnitTestClass2Base : UnitTestBase
{
protected void RunTest3Times(Func<string, IWindsorContainer> getContainer)
{
const string projNameBase = "MyProjectName";
Func<int, string> getProjectName = i => projNameBase + i;
for (var i = 0; i < 3; i++)
{
var pName = getProjectName(i);
GetClass2ForProject(getContainer(pName), pName);
}
}
protected void GetClass2ForProject(IWindsorContainer cont, string projName)
{
var c2 = cont.Resolve<Class2>();
Assert.IsTrue(c2.Project.Name == projName);
Assert.IsTrue(c2.Service2.Project.Name == projName);
Assert.IsTrue(c2.Service2.GetProjectName() == projName);
}
}
/// <summary>
/// This will fail on the second request because we cannot
/// overwrite the earlier registration. And iirc containers can't
/// be altered after the first resolve.
/// </summary>
[TestClass]
public class Attempt_1 : UnitTestClass2Base
{
[TestMethod]
public void Class2_Resolves_Project_Scoped_Requests()
{
RunTest3Times(s =>
{
Cont.Register(Component.For<IProject>().Instance(new Project(s)));
return Cont;
});
}
}
/// <summary>
/// It looks like we have to create a new container for every Project
/// So now the question remains; how do we get to keep using the base IService implementations
/// in the container that is scoped for the IProject?
/// </summary>
[TestClass]
public class Attempt_2 : UnitTestClass2Base
{
static IWindsorContainer CreateContainer(IProject p)
{
var ret = new WindsorContainer();
ret.Register(Component.For<IProject>().Instance(p));
return ret;
}
/// <summary>
/// This will fail because the services in the main
/// container can't access the IProject in the new container
/// </summary>
[TestMethod]
public void Class2_Resolves_Project_Scoped_Requests_1()
{
RunTest3Times(s =>
{
//Add the project container as a Child to the Main container
var projCont = CreateContainer(new Project(s));
Cont.AddChildContainer(projCont);
return Cont;
});
}
/// <summary>
/// Doing the previous approach the other way around works.
/// But now we can only resolve one thing at a time
/// </summary>
[TestMethod]
public void Class2_Resolves_Project_Scoped_Requests_2()
{
IWindsorContainer projCont = null;
//Add the Main container as a Child to the project container
// (in other words set the Parent of Main to Project)
// and then resolve using the main container.
//A container can only have one parent at a time so we can only
// resolve one scoped thing at a time.
RunTest3Times(s =>
{
if (projCont != null)
projCont.RemoveChildContainer(Cont);
projCont = CreateContainer(new Project(s));
projCont.AddChildContainer(Cont);
return Cont;
});
}
/// <summary>
/// The only way around that issue seems to be to register all project-dependent
/// services in the new container. Then re-register all original services
/// in the new container and pass the resolving on to the main container;
/// a maintenance nightmare and especially painful for named registrions.
/// </summary>
[TestMethod]
public void Class2_Resolves_Project_Scoped_Requests_3()
{
Func<IProject, IWindsorContainer> createContainer2 = p =>
{
var contNew = new WindsorContainer();
//Pass resolving of the non-dependent services on to the main container.
// this way it will respect it's lifestyle rules and not create new
// instances of services we wanted to use as a singleton etc.
contNew.Register(Component.For<IService1>().UsingFactoryMethod(() => Cont.Resolve<IService1>()).LifestyleTransient());
contNew.Register(Component.For<Class1>().UsingFactoryMethod(() => Cont.Resolve<Class1>()).LifestyleTransient());
//Register the dependent services directly in the new container so they can access the project
contNew.Register(Component.For<IService2>().ImplementedBy<Service2>().LifestyleTransient());
contNew.Register(Component.For<IService3>().ImplementedBy<Service3>().LifestyleTransient());
contNew.Register(Component.For<Class2>().LifestyleTransient());
contNew.Register(Component.For<IProject>().Instance(p));
return contNew;
};
RunTest3Times(s =>
{
var projCont = createContainer2(new Project(s));
return projCont;
});
}
}
}https://stackoverflow.com/questions/16197371
复制相似问题