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



我正在开发一个使用温莎城堡的 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...
}

相关内容

  • 没有找到相关文章

最新更新