1

DefaultServiceHostFactory から派生したカスタム ServiceHostFactory を含む WCF サービス ライブラリがあります。テスト クライアントにこのファクトリを使用させることができません。「パラメーターなしのコンストラクターが見つかりませんでした」というエラーが表示されます。

ホスティング環境の構成は次のとおりです。

<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 ファイルがないことに注意してください。ファイルレスアクティベーションを使用しようとしています。

これが私のカスタム 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 テスト クライアントを取得するにはどうすればよいですか?

4

2 に答える 2

3

OK、これは可能ですが、きれいではありません...

まず第一に、このシナリオでは「メイン」や「アプリケーションの開始」などではないため、アセンブリがロードされたときにフックする方法が必要です。AppDomain にはAssemblyLoadedと呼ばれる興味深いイベントがあり、うまくいくように見えますが、それにフックする方法は、カスタム アプリ ドメイン マネージャーを定義することです。

新しいアセンブリを作成し、その中にインターフェイスを定義します。これは、次のように一部のクライアントとAppDomainManagerによって実装できます。

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を呼び出したとしましょう。次のように追加できます(必要になるため、すぐに詳細を取得できます)。

gacutil /i AssemblyInitCustomDomainManager.dll
gacutil /l AssemblyInitCustomDomainManager

ここで、WcfServiceHost.exe.config (通常は次の場所にあります: C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE または 64 ビット システムの x86 バージョン) を編集し、ランタイム要素内に次を追加します (詳細については、こちらを参照してください)。この設定):

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

注: 型名、名前空間、アセンブリ名、公開キー、バージョン番号の少なくとも 1 つ (および上記の名前によってはすべて) を変更する必要があります。あなたの場合、彼らが何をする必要があるかを理解できるはずです。

OK、これは簡単でした。Visual Studio で新しい「WCF Service Library」プロジェクトを作成すると、app.config とサービスが作成されます (これは、テストしたいプロジェクトの種類です。そう願っています!)。

まず、サービス ホスト アプリケーションにこれを読み取らせたくないため、app.config からsystem.servicemodel部分を削除します。次に、Service1.cs と IService1.cs を削除します (独自のものを少し作成するため)。後で)。そのインターフェイスを実装する必要があるため、前に作成したアプリ ドメイン マネージャー アセンブリを必ず参照してください。

ここで、新しいファイルを作成し、次のコードを貼り付けます (Castle WCF ファシリティによってホストされる依存関係を持つ単純なサービスがあります)。

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);
        }
    }
}

[実行] をクリックすると、次のようなエラー メッセージが表示されます。

WCF サービス ホストは、サービス メタデータを見つけることができません。これにより、クライアント アプリケーションが正しく実行されない可能性があります。メタデータが有効になっているかどうかを確認してください。終了しますか?

いいえをクリックしてください。

テスト クライアントが起動しますが、残念ながらサービスが含まれていません。add service... を右クリックしてサービスの URL を入力するだけです (コードのインストーラーにある - http://localhost:9777/TestWcfServiceLibrary/Service1 )。

では、WCF テスト クライアント内でホストされる WCF サービスについて説明します。信じないでください。テストして GetData 操作を呼び出すと、結果が表示されるはずです。

ほらね。これが良いアイデアかどうか尋ねた場合...わかりませんが、機能し、あなたが求めていたものだと思います...

于 2012-04-13T07:02:35.333 に答える
1

どこかで読んだ記憶があります!リンク

「WcfSvcHost の最大の欠点は、ホスト インスタンスを開く前にホスト インスタンスにプログラムでアクセスする必要がない、または一度開いたイベント モデルにプログラムでアクセスする必要がない単純なシナリオにしか適していないことです。IIS や Windows Activation Service (WAS )、同等のサービス ホスト ファクトリ サポートはありません。したがって、ベース アドレスを動的に追加する機能、エンドポイントを構成する機能、呼び出しを調整する機能、ホスト レベルでカスタム動作を構成する機能などはありません。最も単純なケースでは、最終的にはホスト インスタンスへのプログラムによるアクセスが必要になるため、私は WAS や専用のセルフ ホストのように、WcfSvcHost を本格的な運用に適したホストとは見なしていません。」

于 2012-04-13T05:13:30.960 に答える