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

时间:2014-08-26 22:29:06

标签: c# asp.net-mvc moq castle-windsor service-locator

我正在使用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...
    }
}

当我开始为这个控制器编写单元测试时(使用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.

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

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.]

显然我没有在Castle上注册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也引用了MembershipService:

public class MyRoleProvider : RoleProvider
{
    private MembershipService MembershipService;

    public override void Initialize( string name, NameValueCollection config )
    {
        MembershipService = ServiceLocator.Current.GetInstance<MembershipService>();

        // blah blah blah...
    }
}

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

public class MyRoleProvider : RoleProvider
{
    private IMembershipService MembershipService;

但它仍会抛出相同的异常(“尝试获取MembershipService类型的实例时发生激活错误...”)。到目前为止我管理的唯一方法就是通过像这样注册IMembershipService MembershipService:

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:\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)

1 个答案:

答案 0 :(得分:1)

您已定义了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) ) );

现在更改单元测试

private Mock<IMembershipService> membershipServiceMock;

[TestInitialize]
public void MyTestInitialize()
{
    membershipServiceMock = new Mock<IMembershipService>();
}

[TestMethod]
public void DoSomethingReturnsWhatever()
{
    var controller = new FooController( membershipServiceMock.Object );

    // etc...
}