如何将我的申请与会员服务分开?

我正在使用Castle Windsor的ASP.NET MVC 4项目。 其中一个控制器依赖于MembershipService:

public class FooController : Controller { private readonly MembershipService MembershipService; public FooController( MembershipService membershipService ) { MembershipService = membershipService; } [Authorize( Roles = "Administrator" )] public ActionResult DoSomething() { var user = MembershipService.GetUser( User.Identity.Name ); // etc... } } 

当我开始为这个控制器编写unit testing时(使用Moq),我遇到了一个问题:

 private Mock membershipServiceMock; [TestInitialize] public void MyTestInitialize() { membershipServiceMock = new Mock(); } [TestMethod] public void DoSomethingReturnsWhatever() { var controller = new FooController( membershipServiceMock.Object ); // etc... } 

测试失败,因为Castle抛出exception:

 Castle.DynamicProxy.InvalidProxyConstructorArgumentsException: Can not instantiate proxy of class: MembershipService. Could not find a parameterless constructor. 

我决定创建一个MembershipService可以实现的接口,因为我们的应用程序通常是如此设计的:

 public interface IMembershipService { User GetCurrentUser( string userName ); } 

我更新了控制器以使用界面,但现在Castle引发了另一个exception:

 [HandlerException: Handler for IMembershipService was not found.] Castle.MicroKernel.Resolvers.DefaultDependencyResolver.TryGetHandlerFromKernel(DependencyModel dependency, CreationContext context) +210 Castle.MicroKernel.Resolvers.DefaultDependencyResolver.ResolveFromKernelByType(CreationContext context, ComponentModel model, DependencyModel dependency) +38 [DependencyResolverException: Missing dependency. Component FooController has a dependency on IMembershipService, which could not be resolved. Make sure the dependency is correctly registered in the container as a service, or provided as inline argument.] 

显然我没有在Castle上注册IMembershipService,所以我改变了我的安装程序:

 container.Register( Component.For() ); 

对此:

 container.Register( Component.For( typeof(IMembershipService) ) .ImplementedBy( typeof(MembershipService) ) ); 

现在Castle引发了另一个例外:

 Configuration Error Parser Error Message: Activation error occurred while trying to get instance of type MembershipService, key "" 

这是web.config文件的相关部分。 违规行是以add name="MyRoleProvider"开头的行:

         

这是因为MyRoleProvider也引用了MembershipService:

 public class MyRoleProvider : RoleProvider { private MembershipService MembershipService; public override void Initialize( string name, NameValueCollection config ) { MembershipService = ServiceLocator.Current.GetInstance(); // blah blah blah... } } 

我尝试更改MyRoleProvider中MembershipService字段的类型,如下所示:

 public class MyRoleProvider : RoleProvider { private IMembershipService MembershipService; 

但它仍会抛出相同的exception(“尝试获取MembershipService类型的实例时发生激活错误…”)。 到目前为止我管理的唯一方法就是将所有IMembershipService MembershipService注册到Castle,如下所示:

 container.Register( Component.For() ); container.Register( Component.For( typeof(IMembershipService) ) .ImplementedBy( typeof(MembershipService) ) ); 

我很确定我不应该两次注册。 那么我怎么能解决这个问题呢? 在这一点上,如果我可以继续编写unit testing,我会很高兴将应用程序与会员服务紧密结合。 但是,如果不涉及主要架构变更,解耦解决方案也会很好。

编辑

堆栈跟踪:

 at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance(Type serviceType, String key) in c:\Projects\CommonServiceLocator\main\Microsoft.Practices.ServiceLocation\ServiceLocatorImplBase.cs:line 53 at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance[TService]() in c:\Projects\CommonServiceLocator\main\Microsoft.Practices.ServiceLocation\ServiceLocatorImplBase.cs:line 90 at MyRoleProvider.Initialize(String name, NameValueCollection config) in d:\Code\MyApplication\MyRoleProvider.cs:line 51 at System.Web.Configuration.ProvidersHelper.InstantiateProvider(ProviderSettings providerSettings, Type providerType) 

您已经定义了IMembershipService

 public interface IMembershipService { User GetCurrentUser( string userName ); } 

现在改变你的FooController

 public class FooController : Controller { private readonly IMembershipService MembershipService; public FooController( IMembershipService membershipService ) { MembershipService = membershipService; } [Authorize( Roles = "Administrator" )] public ActionResult DoSomething() { var user = MembershipService.GetUser( User.Identity.Name ); // etc... } } 

转到并将代码MembershipService中的任何位置更改为IMembershipService,如FooController中所示。 现在,在Castle中注册IMembershipService

 container.Register( Component.For( typeof(IMembershipService) ) .ImplementedBy( typeof(MembershipService) ) ); 

现在改变unit testing

 private Mock membershipServiceMock; [TestInitialize] public void MyTestInitialize() { membershipServiceMock = new Mock(); } [TestMethod] public void DoSomethingReturnsWhatever() { var controller = new FooController( membershipServiceMock.Object ); // etc... }