Как я могу отделить свою заявку от моей службы членства?

1

Я работаю над проектом ASP.NET MVC 4, который использует Castle Windsor. Один из контроллеров имеет зависимость от 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.

Я решил, что должен сделать интерфейс, который может реализовать MemberhipService, потому что как обычно разрабатываются наши приложения:

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 с Castle, поэтому я изменил свой установщик:

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

К этому:

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

Теперь замок бросает еще одно исключение:

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

Я попытался изменить тип поля MembershipService в MyRoleProvider следующим образом:

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)
  • 0
    Почему бы вам не изменить тип с MembershipService на IMembershipService везде, где вы определили MembershipService? Например, в вашем личном FooController только для чтения private readonly IMembershipService MembershipService;
  • 0
    @michaelmoore Ну, я не очень знаком с этим бизнесом по поиску сервисов, но я не могу изменить ServiceLocator.CurrentGetInstance<MembershipService> на ServiceLocator.CurrentGetInstance<IMembershipService> по некоторым причинам.
Показать ещё 3 комментария
Теги:
asp.net-mvc
moq
service-locator
castle-windsor

1 ответ

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. Теперь зарегистрируйтесь 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...
}
  • 0
    Ах, я вижу, что вы печатали ответ, отвечая на мои комментарии. Я уже сделал шаг, который вы упомянули в контроллере, но мне нужно было изменить строку ServiceLocator.CurrentGetInstance<MembershipService> на ServiceLocator.CurrentGetInstance<IMembershipService> после изменения типа поля в MyRoleProvider :)
  • 0
    @Koveras вы можете опубликовать трассировку стека, когда Касл выдает исключение в RoleProvider?
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню