我怎样才能脱钩我的应用程序从我的会员服务? [英] How can I decouple my application from my membership service?

查看:260
本文介绍了我怎样才能脱钩我的应用程序从我的会员服务?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用温莎城堡的ASP.NET MVC 4项目。其中一个控制器对MembershipService依赖:

 公共类FooController的:控制器
{
    私人只读MembershipService MembershipService;    公共FooController的(MembershipService membershipService)
    {
        MembershipService = membershipService;
    }    [授权(角色=管理员)]
    公众的ActionResult DoSomething的()
    {
        VAR用户= MembershipService.GetUser(User.Identity.Name);        //等等...
    }
}

当我开始为这个控制器编写单元测试(使用MOQ),我遇到了一个问题:

 私人模拟< MembershipService> membershipServiceMock;[TestInitialize]
公共无效MyTestInitialize()
{
    membershipServiceMock =新的模拟< MembershipService>();
}[测试方法]
公共无效DoSomethingReturnsWhatever()
{
    VAR控制器=新FooController的(membershipServiceMock.Object);    //等等...
}

测试失败,因为城堡抛出异常:

  Castle.DynamicProxy.InvalidProxyConstructorArgumentsException:无法实例化类的代理:MembershipService。
找不到参数的构造函数。

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

 公共接口IMembershipService
{
    用户GetCurrentUser(字符串用户名);
}

我更新了控制器使用的接口,但现在城堡内引发另一个异常:

  [HandlerException:处理器为IMembershipService未找到]
   Castle.MicroKernel.Resolvers.DefaultDependencyResolver.TryGetHandlerFromKernel(DependencyModel依赖,CreationContext上下文)+210
   Castle.MicroKernel.Resolvers.DefaultDependencyResolver.ResolveFromKernelByType(CreationContext背景下,ComponentModel模型,DependencyModel依赖)+38[DependencyResolverException:缺少依赖。
组件FooController的对IMembershipService的依赖,而无法得到解决。
确保依赖关系在容器正确地登记为一个服务,或内联的参数设置。]

很显然,我没有注册IMembershipService与城堡,所以我改变了我的安装程序从这个:

  container.Register(Component.For< MembershipService>());

要这样:

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

现在城堡抛出另一个异常:

 配置错误
尝试获取类型MembershipService的情况下,关键的发生激活错误:分析器错误信息

下面是web.config文件的相关部分。该问题的行是开头一添加名称=MyRoleProvider

 <身份验证模式=窗口/>
< roleManager启用=真正的defaultProvider =MyRoleProvider>
  <供应商>
    <清/>
    <添加名称=MyRoleProviderTYPE =MyRoleProvider的applicationName =MyApplication的autoCreateRole =真autoCreateUser =真/>
    <添加名称=AspNetWindowsTokenRoleProviderTYPE =System.Web.Security.WindowsTokenRoleProvider的applicationName =//>
  < /供应商>
< / roleManager>

这是因为MyRoleProvider有MembershipService引用,以及:

 公共类MyRoleProvider:RoleProvider
{
    私人MembershipService MembershipService;    公共覆盖无效初始化(字符串名称,NameValueCollection中配置)
    {
        MembershipService = ServiceLocator.Current.GetInstance< MembershipService>();        // 等等等等等等...
    }
}

我试图改变MembershipService字段的类型MyRoleProvider像这样:

 公共类MyRoleProvider:RoleProvider
{
    私人IMembershipService MembershipService;

但它仍然抛出上述同样的异常(试图获得类型MembershipService的实例时发生激活错误...)。到目前为止,我已经成功地得到的一切合作的唯一方法是通过注册两个IMembershipService的的MembershipService与城堡,像这样:

  container.Register(Component.For< MembershipService>());
container.Register(Component.For(typeof运算(IMembershipService))
         .ImplementedBy(typeof运算(MembershipService)));

我是pretty肯定我不应该两次注册的事情。所以,我还能怎么解决这个问题?在这一点上我很乐意留下与紧耦合的会员服务的应用,如果我能只要着手编写单元测试。但是去耦解决方案将是很好的太多,如果它不涉及一个重大变化架构

修改

堆栈跟踪:

 在Microsoft.P​​ractices.ServiceLocation.ServiceLocatorImplBase.GetInstance(类型的serviceType,串键)的c:\\Projects\\CommonServiceLocator\\main\\Microsoft.Practices.ServiceLocation\\ServiceLocatorImplBase.cs:line 53
在Microsoft.P​​ractices.ServiceLocation.ServiceLocatorImplBase.GetInstance [TService]()中c:\\Projects\\CommonServiceLocator\\main\\Microsoft.Practices.ServiceLocation\\ServiceLocatorImplBase.cs:line 90
在MyRoleProvider.Initialize(字符串名称,NameValueCollection中配置)在d:\\ code \\ MyApplication的\\ MyRoleProvider.cs:行51
在System.Web.Configuration.ProvidersHelper.InstantiateProvider(ProviderSettings providerSettings,类型providerType)


解决方案

您已经定义IMembershipService

 公共接口IMembershipService
{
    用户GetCurrentUser(字符串用户名);
}

现在改变你的FooController的

 公共类FooController的:控制器
{
    私人只读IMembershipService MembershipService;    公共FooController的(IMembershipService membershipService)
    {
        MembershipService = membershipService;
    }    [授权(角色=管理员)]
    公众的ActionResult DoSomething的()
    {
        VAR用户= MembershipService.GetUser(User.Identity.Name);        //等等...
    }
}

你去如图FooController的在code MembershipService到IMembershipService改变无处不在。
现在,报名的城堡IMembershipService

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

现在改变单元测试

 私人模拟< IMembershipService> membershipServiceMock;[TestInitialize]
公共无效MyTestInitialize()
{
    membershipServiceMock =新的模拟< IMembershipService>();
}[测试方法]
公共无效DoSomethingReturnsWhatever()
{
    VAR控制器=新FooController的(membershipServiceMock.Object);    //等等...
}

I'm working on an ASP.NET MVC 4 project that uses Castle Windsor. One of the controllers has a dependency on 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...
    }
}

When I started writing unit tests for this controller (using Moq), I ran into a problem:

private Mock<MembershipService> membershipServiceMock;

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

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

    // etc...
}

The test fails because Castle throws an exception:

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

I decided I should make an interface that MembershipService can implement, because that's how our apps are normally designed anyways:

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

I updated the controller to use the interface, but now Castle throws another 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.]

Obviously I'm not registering IMembershipService with Castle, so I changed my installer from this:

container.Register( Component.For<MembershipService>() );

To this:

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

Now Castle throws yet another exception:

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

Here's the relevant section of the web.config file. The offending line is the one that starts with 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>

This is because MyRoleProvider has a reference to MembershipService as well:

public class MyRoleProvider : RoleProvider
{
    private MembershipService MembershipService;

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

        // blah blah blah...
    }
}

I tried changing the type of the MembershipService field in MyRoleProvider like so:

public class MyRoleProvider : RoleProvider
{
    private IMembershipService MembershipService;

But it still throws the same exception above ("Activation error occurred while trying to get instance of type MembershipService..."). The only way I've managed so far to get everything to cooperate is by registering both IMembershipService and MembershipService with Castle like so:

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

I'm pretty sure I'm not supposed to register things twice. So how else can I fix this? At this point I'd be happy with leaving the application tightly coupled to the membership service if I can just get on with writing unit tests. But a decoupling solution would be nice too if it doesn't involve a major architecture change.

Edit

Stack trace:

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)

解决方案

You already defined IMembershipService

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

Now change your 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...
    }
}

Go and change everywhere in your code MembershipService to IMembershipService as shown in FooController. Now, register IMembershipService in Castle

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

Now change Unit Tests

private Mock<IMembershipService> membershipServiceMock;

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

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

    // etc...
}

这篇关于我怎样才能脱钩我的应用程序从我的会员服务?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆