我可以使用WcfFacility与WCF测试客户端?可以使用、客户端、测试、WCF

2023-09-04 02:31:40 作者:八个字的昵称设计

我有一个包含从DefaultServiceHostFactory派生的自定义ServiceHostFactory一个WCF服务库。我不能让测试客户端使用此工厂。我只是得到了无参数的构造函数被发现的错误。

I have a WCF Service Library containing a custom ServiceHostFactory derived from DefaultServiceHostFactory. I can't get the test client to use this factory. I just get the "no parameterless constructor was found" error.

下面是我的托管环境的配置:

Here's my hosting enviroment configuration:

<serviceHostingEnvironment>
  <serviceActivations>
    <add service="TestService.WcfLibrary.TestService"
         relativeAddress="/TestService.svc"
         factory="TestService.WcfLibrary.TestServiceHostFactory, TestService.WcfLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
  </serviceActivations>
</serviceHostingEnvironment>

请注意,我没有一个.svc文件实际。我想使用fileless激活。

Note that I don't have a .svc file actually. I am trying to use fileless activation.

下面是我的自定义ServiceHostFactory:

Here's my custom ServiceHostFactory:

public class TestServiceHostFactory : DefaultServiceHostFactory
{
    public TestServiceHostFactory() : base(CreateKernel()) { }

    private static IKernel CreateKernel()
    {
        WindsorContainer container = new WindsorContainer();

        container.AddFacility<WcfFacility>();

        container.Register(Component.For<TestService>());
        container.Register(Component.For<IServiceManager>().ImplementedBy<ServiceManager>());

        return container.Kernel;
    }
}

看起来永远不会执行这条道路。我怎样才能WCF测试客户端使用个性化的实现?

It looks like this path is never executed. How can I get WCF Test Client to use my custom implementation?

推荐答案

OK,这是可能的,但它AINT pretty的...

OK, this is possible, but it aint pretty...

首先,我们需要一种方法,当组件加载,因为在这种情况下他们没有主或应用程序启动或任何钩研究。有在AppDomain中一个有趣的事件称为 AssemblyLoaded ,看起来像它可能做的伎俩,嗯,但如何挂钩到它,某种程度上,我认为这样做是为了定义一个自定义的应用程序域管理器等等...

First of all we need a way to hook in when the assembly is loaded since in this scenario their is no "main" or "application start" or anything. There is an interesting event on AppDomain called AssemblyLoaded that looks like it may do the trick, hmm, but how to hook into it, a way I thought to do this is to define a custom app domain manager so...

创建一个全新的组件和里面定义一个接口,其可以通过一些客户端和 AppDomainManager 像这样:

Create a brand new assembly and inside it define an interface, which can be implemented by some client and an AppDomainManager like so:

public interface IAssemblyLoaded
{
    void AssemblyLoaded();
}

public class CustomManager : AppDomainManager
{
    public override void InitializeNewDomain(AppDomainSetup appDomainInfo)
    {
        base.InitializeNewDomain(appDomainInfo);

        // Look for any classes that implement IAssemblyLoaded,
        // constuct them (will only work if they have a default constructor)
        // and call the AssemblyLoaded method
        var loaded = typeof (IAssemblyLoaded);
        AppDomain
            .CurrentDomain
            .AssemblyLoad += (s, a) =>
                                 {
                                     var handlers = a
                                         .LoadedAssembly
                                         .GetTypes()
                                         .Where(loaded.IsAssignableFrom);

                                     foreach (var h in handlers)
                                     {
                                         ((IAssemblyLoaded)
                                          Activator.CreateInstance(h))
                                             .AssemblyLoaded();
                                     }
                                 };
    }
}

请确保该组件签署,然后将其添加到GAC。比方说,我叫拼装AssemblyInitCustomDomainManager我可以添加它像这样(我会得到关于它的详细信息马上,因为我需要他们):

Make sure the assembly is signed and then add it to the GAC. Let's say I called the assembly AssemblyInitCustomDomainManager I can add it like so (and I get back the details about it straight away because I will need them):

gacutil /i AssemblyInitCustomDomainManager.dll
gacutil /l AssemblyInitCustomDomainManager

现在编辑 WcfServiceHost.exe.config (通常位于C:\ Program Files文件\微软的Visual Studio 10.0 \ Common7 \ IDE或在64位系统的x86版本),并添加以下在运行元素中(见此处这个设置信息):

Now edit the WcfServiceHost.exe.config (typically located at: C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE or the x86 version on 64 bit systems) and add the following inside the runtime element (see here for info about this settings):

<appDomainManagerType value="AssemblyInitCustomDomainManager.CustomManager" />
<appDomainManagerAssembly value="AssemblyInitCustomDomainManager, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c841b3549556e52a, processorArchitecture=MSIL" />

注意:您将需要修改的至少一个(也可能完全取决于你叫的东西上面):类型名称,命名空间,程序集名称,公钥,版本号。我想你应该能够找出他们需要你的情况。的

好了,这是很容易,现在我们将在Visual Studio中的一个新的WCF服务库项目,它会创建一个app.config,并为我们服务(这是你想要测试权的项目类型,哦,我希望如此!)。

OK, that was easy enough, now we will create a new "WCF Service Library" project in visual studio and it will create an app.config and a service for us (this is the project type you are wanting to test right, oh I hope so!).

首先,删除 system.servicemodel ,因为我们不希望服务的主机应用程序在看完这些内容并删除Service1.cs和IService1.cs部分来自在app.config(因为我打算让我自己有点晚)。请确保您引用先前创建的应用程序域管理器组件,因为你需要实现该接口。

First of all, remove the system.servicemodel part from the app.config since we do not want the service host application to read this in and then delete the Service1.cs and IService1.cs (as I am going to make my own a bit later). Make sure that you reference the app domain manager assembly you created earlier since you need to implement that interface.

现在,创建一个新的文件,并坚持以下code在(我们只是有与城堡WCF基金托管依赖一个简单的服务):

Now, create a new file and stick the following code in (we are just having a simple service with a dependency hosted by the Castle WCF Facility):

using System;
using System.ServiceModel;

using AssemblyInitCustomDomainManager;

using Castle.Facilities.WcfIntegration;
using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
using Castle.Windsor.Installer;

namespace TestWcfServiceLibrary
{
    public class AssemblyInitializedHandler : IAssemblyLoaded
    {
        // This method is called when the assembly loads so we will create the
        // windsor container and run all the installers we find
        public void AssemblyLoaded()
        {            
            new WindsorContainer().Install(FromAssembly.This());            
        }        
    }

    // This installer will set up the services
    public class ServicesInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, 
                            IConfigurationStore store)
        {
            container
                .AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero)
                .Register(Component
                              .For<ITestDependency>()
                              .ImplementedBy<TestDependency>(),
                          Component
                              .For<IService1>()
                              .ImplementedBy<Service1>()
                              .AsWcfService(new DefaultServiceModel(WcfEndpoint
                                                                        .BoundTo(new WSHttpBinding()))
                                                .AddBaseAddresses("http://localhost:9777/TestWcfServiceLibrary/Service1")
                                                .PublishMetadata(m => m.EnableHttpGet())));
        }
    }

    // This is the contract of something we want to make sure is loaded
    // by Windsor
    public interface ITestDependency
    {
        int DoSomething(int value);
    }

    public class TestDependency : ITestDependency
    {
        public int DoSomething(int value)
        {
            return value;
        }
    }

    // Regular WCF service contract
    [ServiceContract]
    public interface IService1
    {
        [OperationContract]
        string GetData(int value);
    }

    // Service implementation - notice it does not have a default 
    // constructor
    public class Service1 : IService1
    {
        private readonly ITestDependency _td;

        public Service1(ITestDependency td)
        {
            _td = td;
        }

        public string GetData(int value)
        {
            int v = _td.DoSomething(value);
            return string.Format(
                "According to our dependency you entered: {0}", v);
        }
    }
}

点击运行,你会得到一个错误信息,说:

Click run and you will get an error message that says:

WCF服务主机无法找到任何服务元数据。这可能导致   客户端应用程序运行不正常。请检查元数据   启用。是否要退出?

WCF Service Host cannot find any service metadata. This may cause the client application to run improperly. Please check if metadata is enabled. Do you want to exit?

不要担心,只需点击没有

测试客户端启动,但遗憾的是它并没有你的服务在里面。不用担心,只需右键点击添加服务...并把在服务(这是在code中的安装程序的URL - 的的http://本地主机:9777 / TestWcfServiceLibrary /服务1 )

The test client starts up, but sadly it does not have your service in it. No worries, just right-click add service... and put in the URL of the service (it is in the installer in the code - http://localhost:9777/TestWcfServiceLibrary/Service1).

和你去那里 - WCF测试客户端内承载WCF服务。不相信我 - 测试它,调用的GetData操作,你应该看到的结果。

And there you go - WCF service hosted inside the WCF Test Client. Don't believe me - test it out, invoke the GetData operation and you should see a result.

你去那里。现在,如果你问,如果这是个好主意......我不知道,但它的工作原理,我认为这是你自找的......

There you go. Now if you asked if this is a good idea...I do not know but it works and I think it is what you asked for...