如何将我的申请与会员服务分开?
我正在使用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... }