ServiceStack AppHost Http Listener Base无法解析服务依赖性

时间:2013-10-10 16:42:47

标签: c# servicestack castle-windsor

我在测试中配置了ServiceStack AppHostHttpListenerBase,目的是运行如下测试:

public class UserProfileBehaviours : BaseTest<UserProfileService>
{
    [Test]
    public void Can_access_secure_service()
    {
        var client = GetClientWithUserPassword();

        var result = ((IRestClient)client).Get(new Dto.View.Requests.UserProfileRequest
             {
                 Slug = "user"
             });

        result.Result.UserAccount.Username.Should().Be("user");
    }
}

我的BaseTest看起来像:

[TestFixture]
public class BaseTest<T>
{
    protected TestAppHostHttpListenerBase<T> AppHost;
    private const string ListeningOn = "http://localhost:82/";
    private const string UserName = "user";
    private const string Password = "p@55word";
    protected readonly Container Container;

    public BaseTest()
    {
        Container = new Funq.Container
        {
            Adapter = new WindsorContainerAdapter()
        };
    }

    [TestFixtureSetUp]
    public void OnTestFixtureSetUp()
    {
        AppHost = new TestAppHostHttpListenerBase<T>();

        AppHost.Init();

        AppHost.Start(ListeningOn);
    }

    [TestFixtureTearDown]
    public void OnTestFixtureTearDown()
    {
        AppHost.Dispose();
    }

    protected IServiceClient GetClient()
    {
        return new JsonServiceClient(ListeningOn);
    }

    protected IServiceClient GetClientWithUserPassword()
    {
        return new JsonServiceClient(ListeningOn)
        {
            UserName = UserName,
            Password = Password
        };
    }
}

然后是我的WindsorContainerAdapter:

public static class CastleWindsor
{
    public static IWindsorContainer InstallFromAssemblies(this IWindsorContainer container, params string[] assemblyNames)
    {
        return container.Install(assemblyNames.Select(
            x => (IWindsorInstaller)new AssemblyInstaller(Assembly.Load(x), new InstallerFactory())).ToArray());
    }
}


public class WindsorContainerAdapter : IContainerAdapter, IDisposable
{
    private readonly IWindsorContainer _container;

    public WindsorContainerAdapter()
    {
        _container = new WindsorContainer("Windsor.config");
        _container.Register(Component.For<IWindsorContainer>().Instance(_container));
        _container.Install(FromAssembly.InThisApplication(), FromAssembly.InDirectory(new ApplicationAssemblyFilter())).InstallFromAssemblies("Web.Api");
        _container.Register(Classes.FromAssemblyNamed("Web.Api").BasedOn(typeof(IRepository<>)).LifestyleSingleton());
        _container.Register(Component.For<IEmailBuilder>().ImplementedBy<EmailBuilder>().LifeStyle.Singleton);
        _container.Register(Component.For<IEmailSender>().ImplementedBy<EmailSender>().LifeStyle.Singleton);
        _container.Register(Component.For<IEmailService>().ImplementedBy<EmailService>());
    }

    public T TryResolve<T>()
    {
        return !_container.Kernel.HasComponent(typeof(T)) ? default(T) :
            Resolve<T>();
    }

    public T Resolve<T>()
    {
        return _container.Resolve<T>();
    }

    public void Dispose()
    {
        _container.Dispose();
    }
}

最后是我的TestAppHostHttpListener

public class TestAppHostHttpListenerBase<T> : AppHostHttpListenerBase
{
    public const string WebHostUrl = "http://localhost:82/";
    private InMemoryAuthRepository _userRep;
    private const string UserName = "user";
    private const string Password = "p@55word";
    public const string LoginUrl = "specialLoginPage.html";

    public TestAppHostHttpListenerBase()
        : base("Validation Tests", typeof(T).Assembly)
    {
    }

    public override void Configure(Container container)
    {

        var appSettings = new AppSettings();

        SetConfig(new EndpointHostConfig { WebHostUrl = WebHostUrl });

        Plugins.Add(new AuthFeature(
                        () =>
                        new AuthUserSession(),
                        new IAuthProvider[]
                            {
                                new BasicAuthProvider(),
                                new CredentialsAuthProvider(),
                                new TwitterAuthProvider(appSettings),
                                new FacebookAuthProvider(appSettings)
                            }, "~/" + LoginUrl));

        container.Register<ICacheClient>(new MemoryCacheClient());
        _userRep = new InMemoryAuthRepository();
        container.Register<IUserAuthRepository>(_userRep);
        CreateUser(1, UserName, null, Password, new List<string> { "TheRole" }, new List<string> { "ThePermission" });
    }

    private void CreateUser(int id, string username, string email, string password, List<string> roles = null, List<string> permissions = null)
    {
        string hash;
        string salt;
        new SaltedHash().GetHashAndSaltString(password, out hash, out salt);

        if (_userRep.GetUserAuthByUserName(username) == null)
        {
            _userRep.CreateUserAuth(new UserAuth
            {
                Id = id,
                DisplayName = "DisplayName",
                Email = email ?? "as@if{0}.com".Fmt(id),
                UserName = username,
                FirstName = "FirstName",
                LastName = "LastName",
                PasswordHash = hash,
                Salt = salt,
                Roles = roles,
                Permissions = permissions
            }, password);
        }
    }
}

在配置容器时,我可以看到UserAccountRepository有一个组件 - 如果该组件是UserProfileService的依赖项,则客户端会收到一个异常,说明自动连接的依赖性不能得到解决。 我不明白的是AppHostHttpListenerBase从哪里获取容器?

我的Windsor适配器永远不会被要求Resolve存储库的组件。

如何给容器AppHostHttpListenerBase以便它可以解决这些依赖?或者我需要以另一种方式配置它吗?

1 个答案:

答案 0 :(得分:1)

尝试在TestAppHostHttpListenerBase.Configure方法的顶部设置容器适配器,而不是在BaseTest类中设置:

public override void Configure(Container container)
{
    container.Adapter = new WindsorContainerAdapter();
    ...
}

Container方法的TestAppHostHttpListenerBase.Configure对象是在TestAppHostHttpListenerBase的基础构造函数中创建的;你没有直接控制它的创建。这是您需要使用适配器等自定义的容器实例

如果您还需要在UserProfileBehaviours或其他单元测试类中使用相同的IoC容器,我认为您可以通过静态EndpointHost.Container属性引用它,并消除正在进行的额外容器实例在BaseTest构造函数中创建。如上所述,在OnTestFixtureSetUp中实例化AppHost对象后,容器对象将变为可用。