我正在开发一个使用温莎城堡的 ASP.NET MVC 4项目。其中一个控制器依赖于成员服务:
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...
}
}
当我开始为此控制器编写单元测试(使用 Moq(时,我遇到了一个问题:
private Mock<MembershipService> membershipServiceMock;
[TestInitialize]
public void MyTestInitialize()
{
membershipServiceMock = new Mock<MembershipService>();
}
[TestMethod]
public void DoSomethingReturnsWhatever()
{
var controller = new FooController( membershipServiceMock.Object );
// etc...
}
测试失败,因为 Castle 抛出异常:
Castle.DynamicProxy.InvalidProxyConstructorArgumentsException: Can not instantiate proxy of class: MembershipService.
Could not find a parameterless constructor.
我决定我应该做一个MemberService可以实现的接口,因为无论如何,我们的应用程序通常是这样设计的:
public interface IMembershipService
{
User GetCurrentUser( string userName );
}
我更新了控制器以使用该接口,但现在 Castle 抛出了另一个异常:
[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.]
显然我没有在城堡注册IMembershipService,所以我从这里更改了我的安装程序:
container.Register( Component.For<MembershipService>() );
对此:
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"
开头的行:
<authentication mode="Windows" />
<roleManager enabled="true" defaultProvider="MyRoleProvider">
<providers>
<clear />
<add name="MyRoleProvider" type="MyRoleProvider" applicationName="MyApplication" autoCreateRole="true" autoCreateUser="true" />
<add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
</providers>
</roleManager>
这是因为MyRoleProvider也引用了MemberService:
public class MyRoleProvider : RoleProvider
{
private MembershipService MembershipService;
public override void Initialize( string name, NameValueCollection config )
{
MembershipService = ServiceLocator.Current.GetInstance<MembershipService>();
// blah blah blah...
}
}
我尝试更改MyRoleProvider中MemberService字段的类型,如下所示:
public class MyRoleProvider : RoleProvider
{
private IMembershipService MembershipService;
但它仍然抛出上述相同的异常("尝试获取 MemberService 类型的实例时发生激活错误......").到目前为止,我设法让一切合作的唯一方法是在 Castle 注册 IMembershipService 和 MemberService,如下所示:
container.Register( Component.For<MembershipService>() );
container.Register( Component.For( typeof(IMembershipService) )
.ImplementedBy( typeof(MembershipService) ) );
我很确定我不应该注册两次。那么我还能如何解决这个问题呢?在这一点上,如果我能继续编写单元测试,我很乐意将应用程序与成员资格服务紧密耦合。但是,如果解耦解决方案不涉及重大的架构更改,那么解耦解决方案也会很好。
编辑
堆栈跟踪:
at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance(Type serviceType, String key) in c:ProjectsCommonServiceLocatormainMicrosoft.Practices.ServiceLocationServiceLocatorImplBase.cs:line 53
at Microsoft.Practices.ServiceLocation.ServiceLocatorImplBase.GetInstance[TService]() in c:ProjectsCommonServiceLocatormainMicrosoft.Practices.ServiceLocationServiceLocatorImplBase.cs:line 90
at MyRoleProvider.Initialize(String name, NameValueCollection config) in d:CodeMyApplicationMyRoleProvider.cs:line 51
at System.Web.Configuration.ProvidersHelper.InstantiateProvider(ProviderSettings providerSettings, Type providerType)
您已经定义了 IMembershipService
public interface IMembershipService
{
User GetCurrentUser( string userName );
}
现在更换您的 Foo控制器
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...
}
}
去把代码中的任何地方都改成IMembershipService,如FooController所示。现在,在城堡注册
container.Register( Component.For( typeof(IMembershipService) )
.ImplementedBy( typeof(MembershipService) ) );
现在更改单元测试
private Mock<IMembershipService> membershipServiceMock;
[TestInitialize]
public void MyTestInitialize()
{
membershipServiceMock = new Mock<IMembershipService>();
}
[TestMethod]
public void DoSomethingReturnsWhatever()
{
var controller = new FooController( membershipServiceMock.Object );
// etc...
}